Skip to content

ADFA-2741: ensure INTERNET permission is added to child apps during debugging sessions#1025

Open
itsaky-adfa wants to merge 18 commits intostagefrom
fix/ADFA-2741
Open

ADFA-2741: ensure INTERNET permission is added to child apps during debugging sessions#1025
itsaky-adfa wants to merge 18 commits intostagefrom
fix/ADFA-2741

Conversation

@itsaky-adfa
Copy link
Contributor

@itsaky-adfa itsaky-adfa commented Feb 27, 2026

See ADFA-2741 for more details.

Signed-off-by: Akash Yadav <akashyadav@appdevforall.org>
Signed-off-by: Akash Yadav <akashyadav@appdevforall.org>
…errors

Signed-off-by: Akash Yadav <akashyadav@appdevforall.org>
@itsaky-adfa itsaky-adfa requested a review from a team February 27, 2026 14:07
@itsaky-adfa itsaky-adfa self-assigned this Feb 27, 2026
Signed-off-by: Akash Yadav <akashyadav@appdevforall.org>
The connector is not the root cause of the issue.

Signed-off-by: Akash Yadav <akashyadav@appdevforall.org>
Signed-off-by: Akash Yadav <akashyadav@appdevforall.org>
@itsaky-adfa itsaky-adfa marked this pull request as ready for review March 3, 2026 15:13
@itsaky-adfa itsaky-adfa changed the title ADFA-2741: enforce IPv4 server socket for SocketListening connector ADFA-2741: ensure INTERNET permission is added to child apps during debugging sessions Mar 3, 2026
Signed-off-by: Akash Yadav <akashyadav@appdevforall.org>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 3, 2026

📝 Walkthrough

Release Notes

  • JDWP debugging support
    • Adds JdwpPlugin and JdwpManifestTransformerTask to automatically add android.permission.INTERNET to merged manifests for debuggable variants when JDWP is enabled.
  • Centralized Gradle plugin configuration
    • New GradlePluginConfig exposes properties including cotg.jdwp.enabled, androidide.logsender.isEnabled, androidide.logsender.aar, and internal test/maven-local keys; LogSenderConfig was removed and its constants moved to GradlePluginConfig.
  • Gradle build integration
    • GradleBuildService now injects a -P flag to enable JDWP for debug builds and accepts enableJdwp/enableLogSender flags when assembling Gradle extra args.
  • LogSender plugin refactor
    • Simplified variant handling using onDebuggableVariants; runtime AAR injection streamlined and earlier complex variant-scanning removed.
  • New Gradle DSL helpers
    • ApplicationAndroidComponentsExtension.onDebuggableVariants(action) and Variant.generateTaskName(prefix, suffix) added to simplify plugin code.
  • JDWP runtime and tooling tweaks
    • JdwpOptions added (JDWP_ENABLED, DEFAULT_JDWP_HOST, CONNECTOR_LOCAL_ADDR, etc.) and JavaLanguageServer/JavaDebugAdapter enhanced with additional JDWP-related logging and connector/address handling.
  • Tests and defaults
    • Plugin tests updated to reflect new logsender default/explicit behavior.
  • Shizuku IPC simplification
    • Replaced BinderContainer Parcelable wrapper with direct putBinder/getBinder usage across manager/provider/starter/server code paths.
  • Miscellaneous
    • Resource-management improvement (use-block for AdbPairingClient), minor refactors/typo fixes, and adjusted default Gradle memory/configuration values in gradle.properties.

Risks & best-practice notes

  • Breaking change: LogSenderConfig removal
    • Any external code referencing LogSenderConfig or its constants will break; migrate usages to GradlePluginConfig immediately.
  • Manifest manipulation caveats
    • The manifest transformer edits merged manifests at build time. Potential risks: namespace handling errors, duplicate permissions, or interactions with manifest-merge steps. Test across variants and complex merge scenarios.
  • Conditional permission injection and security
    • INTERNET permission is injected only when JDWP is enabled for debug variants. Confirm this behavior aligns with security policies and that production/non-debug builds remain unaffected.
  • Build performance and caching
    • Per-variant manifest transformation adds work to debug builds; verify incremental build behavior and caching for large projects.
  • IPC compatibility
    • Switching from BinderContainer Parcelable to direct Binder in IPC payloads changes the bundle format; ensure all communicating components are updated to remain compatible.

Walkthrough

Consolidates plugin configuration constants into GradlePluginConfig, adds JDWP support via a new JdwpPlugin that injects INTERNET permission into debuggable manifests, wires JDWP toggle through GradleBuildService, refactors LogSender plugin/DSL, and replaces BinderContainer parcelable usage with direct IBinder passing across Shizuku components.

Changes

Cohort / File(s) Summary
Config constants
gradle-plugin-config/src/main/java/com/itsaky/androidide/tooling/api/GradlePluginConfig.java, gradle-plugin-config/src/main/java/com/itsaky/androidide/tooling/api/LogSenderConfig.java
Added GradlePluginConfig with JDWP/logsender/internal properties; removed LogSenderConfig, consolidating constants.
Build service
app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.kt
Added JDWP Gradle arg and evaluate enableJdwp with build type; changed getGradleExtraArgs signature to accept enableJdwp/enableLogSender.
Gradle plugins
gradle-plugin/src/main/java/com/itsaky/androidide/gradle/AndroidIDEGradlePlugin.kt, gradle-plugin/src/main/java/com/itsaky/androidide/gradle/LogSenderPlugin.kt
Apply JDWP plugin when cotg.jdwp.enabled is true; refactor LogSender gating to use onDebuggableVariants and GradlePluginConfig constants; simplified variant handling.
JDWP feature
gradle-plugin/src/main/java/com/itsaky/androidide/gradle/JdwpPlugin.kt
New JdwpPlugin and JdwpManifestTransformerTask that read merged manifest, append android.permission.INTERNET if missing, and emit updated manifest.
Common helpers
gradle-plugin/src/main/java/com/itsaky/androidide/gradle/common.kt
Added onDebuggableVariants DSL helper and Variant.generateTaskName; switched test-env property source to GradlePluginConfig.
Plugin tests
gradle-plugin/src/test/java/com/itsaky/androidide/gradle/AndroidIDEPluginTest.kt
Updated imports to GradlePluginConfig; added default-enable test and renamed explicit-case test; retained dependency test.
LSP / JDWP client
lsp/java/src/main/java/com/itsaky/androidide/lsp/java/JavaLanguageServer.kt, .../JavaDebugAdapter.kt, .../JdwpOptions.kt
Added JDWP enable flag/constants, improved connector selection and listener logs, added CONNECTOR_LOCAL_ADDR and host defaults.
Listener formatting
lsp/java/src/main/java/com/itsaky/androidide/lsp/java/debug/ListenerState.kt
Formatting-only change (trailing comma); no behavior change.
Shizuku binder flow
subprojects/.../ShizukuManagerProvider.kt, .../AdbPairingService.kt, subprojects/.../ShizukuProvider.java, subprojects/.../ShizukuService.java, subprojects/.../ServiceStarter.java
Replaced BinderContainer parcelable with direct IBinder via putBinder/getBinder; adjusted usages and resource handling (.use for AdbPairingClient).
AGP init script
gradle-plugin/src/main/java/com/itsaky/androidide/gradle/AndroidIDEInitScriptPlugin.kt
Comment clarification only.
COTG settings
gradle-plugin/src/main/java/com/itsaky/androidide/gradle/COTGSettingsPlugin.kt
Switched internal property imports to GradlePluginConfig; minor formatting changes.
Gradle properties
gradle.properties
Adjusted JVM/AAPT2 memory settings and enabled/changed several Gradle and Android tooling flags.

Sequence Diagram(s)

sequenceDiagram
    participant GBS as GradleBuildService
    participant AGP as AndroidIDEGradlePlugin
    participant JP as JdwpPlugin
    participant JMT as JdwpManifestTransformerTask
    participant Manifest as AndroidManifest.xml

    GBS->>AGP: Start build (includes -Pcotg.jdwp.enabled flag)
    AGP->>AGP: Check `cotg.jdwp.enabled`
    alt JDWP enabled
        AGP->>JP: Apply JdwpPlugin
        JP->>JP: Register onDebuggableVariants callback
        JP->>JMT: Register transformer task per debuggable variant
        JMT->>Manifest: Read merged manifest (InputFile)
        JMT->>JMT: Check for android.permission.INTERNET
        alt Permission missing
            JMT->>Manifest: Insert INTERNET permission node
        end
        JMT->>Manifest: Write updated manifest (OutputFile)
        JP->>AGP: Wire task as MERGED_MANIFEST transformation
    else JDWP disabled
        AGP-->>GBS: Skip JdwpPlugin
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • jomen-adfa
  • jatezzz

Poem

🥕 I hopped through configs, constants in tow,
I planted JDWP where debuggables grow,
Binders unwrapped, now simple and neat,
Manifests gained permission—what a treat!
Logs whisper secrets as builds gently flow.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly relates to the main objective: adding INTERNET permission to child apps during debugging sessions via the new JdwpPlugin and related changes.
Description check ✅ Passed The description references the issue ADFA-2741 which corresponds to the changeset's primary objective of ensuring INTERNET permission is added during debug sessions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/ADFA-2741

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (4)
lsp/java/src/main/java/com/itsaky/androidide/lsp/java/debug/JavaDebugAdapter.kt (1)

615-618: Rename client to reflect actual type (VirtualMachine).

Line 615 stores a VM instance, not a debug client. Renaming avoids log/debug confusion during incident triage.

Small clarity diff
-				val client = listenerState.accept()
-				logger.debug("client: {}", client)
-
-				onConnect(client)
+				val connectedVm = listenerState.accept()
+				logger.debug("Accepted VM: {}", connectedVm)
+
+				onConnect(connectedVm)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@lsp/java/src/main/java/com/itsaky/androidide/lsp/java/debug/JavaDebugAdapter.kt`
around lines 615 - 618, The local variable named client actually holds a
VirtualMachine from listenerState.accept(); rename it to virtualMachine (or vm)
to reflect its type, update the debug log to reference virtualMachine, and pass
virtualMachine into onConnect(virtualMachine); also update any other local uses
in the surrounding method to use the new name to avoid confusion during
debugging and incident triage.
gradle-plugin/src/main/java/com/itsaky/androidide/gradle/LogSenderPlugin.kt (2)

67-70: Redundant plugin check can be removed.

After the early return at Line 45-47, the check(plugins.hasPlugin(APP_PLUGIN)) block at Line 68-70 is unreachable in normal flow.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gradle-plugin/src/main/java/com/itsaky/androidide/gradle/LogSenderPlugin.kt`
around lines 67 - 70, Remove the redundant plugin presence check inside the
target.run block: because the apply method already returns early when the
project is not an Android application, the check(plugins.hasPlugin(APP_PLUGIN))
call (inside target.run) is unreachable; delete that check and its accompanying
error message so the apply logic relies on the earlier early-return guard,
keeping the rest of target.run intact.

89-95: Avoid silent no-op for unsupported AGP variant wrappers.

Line 89-95 only handles two concrete variant types; other implementations are ignored silently. Fail fast (or at least warn) so LogSender wiring doesn’t disappear unnoticed after AGP updates.

♻️ Suggested hardening
 	private fun ApplicationVariant.withRuntimeConfiguration(action: Configuration.() -> Unit) {
 		if (this is ApplicationVariantImpl) {
 			variantDependencies.runtimeClasspath.action()
 		} else if (this is AnalyticsEnabledApplicationVariant) {
 			delegate.withRuntimeConfiguration(action)
+		} else {
+			throw GradleException(
+				"Unsupported ApplicationVariant type for runtime configuration: ${this::class.qualifiedName}",
+			)
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gradle-plugin/src/main/java/com/itsaky/androidide/gradle/LogSenderPlugin.kt`
around lines 89 - 95, The helper extension
ApplicationVariant.withRuntimeConfiguration currently only handles
ApplicationVariantImpl and AnalyticsEnabledApplicationVariant and silently
no-ops for any other implementations; change it to fail fast or at least warn:
after the existing branches add an else that either throws an
IllegalStateException with a clear message referencing withRuntimeConfiguration
and the runtime variant type, or logs a warning via the plugin logger including
the actual runtime class name so wiring failures are visible; keep the existing
behavior for ApplicationVariantImpl (call
variantDependencies.runtimeClasspath.action()) and for
AnalyticsEnabledApplicationVariant (call
delegate.withRuntimeConfiguration(action)) but ensure the fallback surfaces the
unexpected variant implementation.
gradle-plugin/src/main/java/com/itsaky/androidide/gradle/JdwpPlugin.kt (1)

48-49: Add @PathSensitive(PathSensitivity.RELATIVE) to improve build cache portability.

The mergedManifest input file should include @PathSensitive(PathSensitivity.RELATIVE) to make cache keys independent of absolute machine paths, enabling better cache sharing across different environments.

Proposed refactor
 import org.gradle.api.tasks.InputFile
 import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
@@
 	`@get`:InputFile
+	`@get`:PathSensitive(PathSensitivity.RELATIVE)
 	abstract val mergedManifest: RegularFileProperty
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gradle-plugin/src/main/java/com/itsaky/androidide/gradle/JdwpPlugin.kt`
around lines 48 - 49, Add path sensitivity to the mergedManifest input to make
build cache keys relative instead of absolute: annotate the mergedManifest
property with `@get`:PathSensitive(PathSensitivity.RELATIVE) (in addition to the
existing `@get`:InputFile) in JdwpPlugin.kt and add the necessary imports for
PathSensitive and PathSensitivity from org.gradle.api.tasks so the cache becomes
portable across machines.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.kt`:
- Line 453: The line unconditionally adds the JDWP flag
(extraArgs.add("-P${PROPERTY_JDWP_ENABLED}=${JdwpOptions.JDWP_ENABLED}")),
enabling JDWP for all builds; change this so the property is only added when the
build was launched for debugging (e.g., check the debug intent or the Build/Run
request's debug flag) and skip adding the property for normal runs. Locate where
extraArgs is assembled in GradleBuildService (the method that constructs
extraArgs) and wrap the add(...) call behind a conditional that verifies the
debug intent (use the existing debug-indicating field or method on the
request/context) so PROPERTY_JDWP_ENABLED is only appended during debug
launches.

In `@gradle-plugin/src/main/java/com/itsaky/androidide/gradle/JdwpPlugin.kt`:
- Around line 59-73: The XML parser is created without namespace awareness and
without secure parsing features, so getNamedItemNS(...) in the uses-permission
loop (see DocumentBuilderFactory.newInstance() and getNamedItemNS usage) will
fail to find android:name attributes and the code is vulnerable to XXE; fix by
configuring the DocumentBuilderFactory instance before calling
newDocumentBuilder(): set isNamespaceAware = true, enable secure processing and
disable external entity expansion (setFeature for
"http://apache.org/xml/features/disallow-doctype-decl",
"http://xml.org/sax/features/external-general-entities",
"http://xml.org/sax/features/external-parameter-entities" to true/false as
appropriate), setXIncludeAware = false and setExpandEntityReferences = false so
namespace lookups via getNamedItemNS(...) work and XXE is mitigated.
- Around line 22-24: The current early-return using
target.plugins.hasPlugin(APP_PLUGIN) in JdwpPlugin is order-dependent; replace
it with target.pluginManager.withPlugin(APP_PLUGIN) and move the manifest
transformer registration (the block that registers the manifest transformer in
JdwpPlugin) into the withPlugin callback so it runs when the Android application
plugin is applied, regardless of load order; keep APP_PLUGIN as the identifier
and ensure any references to project/target used inside the registration are
captured from the surrounding scope or re-resolved inside the callback.

In
`@lsp/java/src/main/java/com/itsaky/androidide/lsp/java/debug/JavaDebugAdapter.kt`:
- Around line 121-127: The code is tightly coupled to
com.sun.tools.jdi.SocketListeningConnector; widen selection to any JDI
ListeningConnector that uses a socket transport and relax
ListenerState.connector's type: replace the
filterIsInstance<SocketListeningConnector>() call on vmm.listeningConnectors()
with a filter that checks connector.transport().name() (e.g., .filter {
it.transport().name().contains("socket", ignoreCase = true) }.firstOrNull()),
and change ListenerState.connector from the concrete SocketListeningConnector
type to the ListeningConnector base type so only ListeningConnector methods are
used.

In `@lsp/java/src/main/java/com/itsaky/androidide/lsp/java/JavaLanguageServer.kt`:
- Around line 141-143: The log message in JavaLanguageServer.kt (inside the else
branch that logs JDWP status) has a typo "Not connection to debug client. JDWP
disabled." — update the string passed to log.info (the message in the else
branch) to correct the grammar to "Not connecting to debug client. JDWP
disabled." so the log reads clearly.

---

Nitpick comments:
In `@gradle-plugin/src/main/java/com/itsaky/androidide/gradle/JdwpPlugin.kt`:
- Around line 48-49: Add path sensitivity to the mergedManifest input to make
build cache keys relative instead of absolute: annotate the mergedManifest
property with `@get`:PathSensitive(PathSensitivity.RELATIVE) (in addition to the
existing `@get`:InputFile) in JdwpPlugin.kt and add the necessary imports for
PathSensitive and PathSensitivity from org.gradle.api.tasks so the cache becomes
portable across machines.

In `@gradle-plugin/src/main/java/com/itsaky/androidide/gradle/LogSenderPlugin.kt`:
- Around line 67-70: Remove the redundant plugin presence check inside the
target.run block: because the apply method already returns early when the
project is not an Android application, the check(plugins.hasPlugin(APP_PLUGIN))
call (inside target.run) is unreachable; delete that check and its accompanying
error message so the apply logic relies on the earlier early-return guard,
keeping the rest of target.run intact.
- Around line 89-95: The helper extension
ApplicationVariant.withRuntimeConfiguration currently only handles
ApplicationVariantImpl and AnalyticsEnabledApplicationVariant and silently
no-ops for any other implementations; change it to fail fast or at least warn:
after the existing branches add an else that either throws an
IllegalStateException with a clear message referencing withRuntimeConfiguration
and the runtime variant type, or logs a warning via the plugin logger including
the actual runtime class name so wiring failures are visible; keep the existing
behavior for ApplicationVariantImpl (call
variantDependencies.runtimeClasspath.action()) and for
AnalyticsEnabledApplicationVariant (call
delegate.withRuntimeConfiguration(action)) but ensure the fallback surfaces the
unexpected variant implementation.

In
`@lsp/java/src/main/java/com/itsaky/androidide/lsp/java/debug/JavaDebugAdapter.kt`:
- Around line 615-618: The local variable named client actually holds a
VirtualMachine from listenerState.accept(); rename it to virtualMachine (or vm)
to reflect its type, update the debug log to reference virtualMachine, and pass
virtualMachine into onConnect(virtualMachine); also update any other local uses
in the surrounding method to use the new name to avoid confusion during
debugging and incident triage.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 34f24db and f0a7a2e.

📒 Files selected for processing (18)
  • app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.kt
  • gradle-plugin-config/src/main/java/com/itsaky/androidide/tooling/api/GradlePluginConfig.java
  • gradle-plugin-config/src/main/java/com/itsaky/androidide/tooling/api/LogSenderConfig.java
  • gradle-plugin/src/main/java/com/itsaky/androidide/gradle/AndroidIDEGradlePlugin.kt
  • gradle-plugin/src/main/java/com/itsaky/androidide/gradle/AndroidIDEInitScriptPlugin.kt
  • gradle-plugin/src/main/java/com/itsaky/androidide/gradle/JdwpPlugin.kt
  • gradle-plugin/src/main/java/com/itsaky/androidide/gradle/LogSenderPlugin.kt
  • gradle-plugin/src/main/java/com/itsaky/androidide/gradle/common.kt
  • gradle-plugin/src/test/java/com/itsaky/androidide/gradle/AndroidIDEPluginTest.kt
  • lsp/java/src/main/java/com/itsaky/androidide/lsp/java/JavaLanguageServer.kt
  • lsp/java/src/main/java/com/itsaky/androidide/lsp/java/debug/JavaDebugAdapter.kt
  • lsp/java/src/main/java/com/itsaky/androidide/lsp/java/debug/JdwpOptions.kt
  • lsp/java/src/main/java/com/itsaky/androidide/lsp/java/debug/ListenerState.kt
  • subprojects/shizuku-manager/src/main/java/moe/shizuku/manager/ShizukuManagerProvider.kt
  • subprojects/shizuku-manager/src/main/java/moe/shizuku/manager/adb/AdbPairingService.kt
  • subprojects/shizuku-provider/src/main/java/rikka/shizuku/ShizukuProvider.java
  • subprojects/shizuku-server/src/main/java/rikka/shizuku/server/ShizukuService.java
  • subprojects/shizuku-starter/src/main/java/moe/shizuku/starter/ServiceStarter.java
💤 Files with no reviewable changes (1)
  • gradle-plugin-config/src/main/java/com/itsaky/androidide/tooling/api/LogSenderConfig.java

Signed-off-by: Akash Yadav <akashyadav@appdevforall.org>
Signed-off-by: Akash Yadav <akashyadav@appdevforall.org>
Signed-off-by: Akash Yadav <akashyadav@appdevforall.org>
Signed-off-by: Akash Yadav <akashyadav@appdevforall.org>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
gradle-plugin/src/main/java/com/itsaky/androidide/gradle/JdwpPlugin.kt (1)

20-45: 🛠️ Refactor suggestion | 🟠 Major

Use pluginManager.withPlugin(APP_PLUGIN) to avoid order-dependent no-op.

The current hasPlugin() check at lines 23-25 is order-dependent. If JdwpPlugin is applied before the Android application plugin, the check returns false and the manifest transformer is never registered.

Also, the .let { } at line 45 is a no-op that can be removed.

♻️ Proposed fix
 override fun apply(target: Project) =
-	target
-		.run {
-			if (!target.plugins.hasPlugin(APP_PLUGIN)) {
-				return
-			}
-
-			logger.info("Applying {} to project '${target.path}'", JdwpPlugin::class.simpleName)
-
-			extensions.getByType(ApplicationAndroidComponentsExtension::class.java).apply {
-				onDebuggableVariants { variant ->
-					val jdwpManifestTransformer =
-						tasks.register(
-							variant.generateTaskName("transform", "JdwpManifest"),
-							JdwpManifestTransformerTask::class.java,
-						)
-
-					variant.artifacts
-						.use(jdwpManifestTransformer)
-						.wiredWithFiles(
-							taskInput = JdwpManifestTransformerTask::mergedManifest,
-							taskOutput = JdwpManifestTransformerTask::updatedManifest,
-						).toTransform(SingleArtifact.MERGED_MANIFEST)
-				}
-			}
-		}.let { }
+	target.pluginManager.withPlugin(APP_PLUGIN) {
+		target.logger.info("Applying {} to project '${target.path}'", JdwpPlugin::class.simpleName)
+		target.extensions.getByType(ApplicationAndroidComponentsExtension::class.java).apply {
+			onDebuggableVariants { variant ->
+				val jdwpManifestTransformer =
+					target.tasks.register(
+						variant.generateTaskName("transform", "JdwpManifest"),
+						JdwpManifestTransformerTask::class.java,
+					)
+
+				variant.artifacts
+					.use(jdwpManifestTransformer)
+					.wiredWithFiles(
+						taskInput = JdwpManifestTransformerTask::mergedManifest,
+						taskOutput = JdwpManifestTransformerTask::updatedManifest,
+					).toTransform(SingleArtifact.MERGED_MANIFEST)
+			}
+		}
+	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gradle-plugin/src/main/java/com/itsaky/androidide/gradle/JdwpPlugin.kt`
around lines 20 - 45, Replace the order-dependent hasPlugin(APP_PLUGIN) check
with pluginManager.withPlugin(APP_PLUGIN) so the registration runs even if the
Android app plugin is applied later: move the extension look-up and
onDebuggableVariants registration into a pluginManager.withPlugin(APP_PLUGIN)
block and register the JdwpManifestTransformerTask (referencing
JdwpManifestTransformerTask, onDebuggableVariants, and
SingleArtifact.MERGED_MANIFEST) inside that block; also remove the trailing .let
{ } no-op after the apply method.
🧹 Nitpick comments (3)
gradle-plugin/src/main/java/com/itsaky/androidide/gradle/COTGSettingsPlugin.kt (1)

59-63: Optional: .toList() is redundant.

split(File.pathSeparatorChar) already returns List<String> in Kotlin, so the .toList() call on line 62 can be removed.

♻️ Proposed simplification
 			isTestEnv to
 				mavenLocalRepos
 					.split(File.pathSeparatorChar)
-					.toList()
 					.filter { it.isNotBlank() }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@gradle-plugin/src/main/java/com/itsaky/androidide/gradle/COTGSettingsPlugin.kt`
around lines 59 - 63, The chain building the map entry for isTestEnv uses
mavenLocalRepos.split(File.pathSeparatorChar).toList() but split already returns
a List<String>, so remove the redundant .toList() call in COTGSettingsPlugin
(the expression that references isTestEnv and mavenLocalRepos) — simply use
mavenLocalRepos.split(File.pathSeparatorChar).filter { it.isNotBlank() } to
produce the desired list.
app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.kt (1)

448-451: Consider using false as default for enableJdwp.

The default value JdwpOptions.JDWP_ENABLED evaluates to true (it's a constant). While this private function currently has a single call site with an explicit parameter, a safer default would be false to ensure JDWP enablement is always an explicit decision.

♻️ Suggested change
 private fun getGradleExtraArgs(
-	enableJdwp: Boolean = JdwpOptions.JDWP_ENABLED,
+	enableJdwp: Boolean = false,
 	enableLogSender: Boolean = DevOpsPreferences.logsenderEnabled,
 ): List<String> {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.kt`
around lines 448 - 451, Change the default for the enableJdwp parameter in
getGradleExtraArgs from JdwpOptions.JDWP_ENABLED to false so JDWP is only
enabled when explicitly requested; update the function signature of
getGradleExtraArgs(enableJdwp: Boolean = false, enableLogSender: Boolean =
DevOpsPreferences.logsenderEnabled) and ensure any call sites that relied on the
implicit default pass the intended boolean explicitly (refer to
getGradleExtraArgs and the enableJdwp parameter).
gradle-plugin/src/main/java/com/itsaky/androidide/gradle/JdwpPlugin.kt (1)

100-105: Enable secure processing on TransformerFactory for consistency.

While the risk is lower when writing output, enabling FEATURE_SECURE_PROCESSING on the TransformerFactory maintains defense-in-depth and ensures consistency with the secure DocumentBuilderFactory configuration above.

🛡️ Proposed fix
-	val transformer = TransformerFactory.newInstance().newTransformer()
+	val transformerFactory = TransformerFactory.newInstance().apply {
+		setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
+	}
+	val transformer = transformerFactory.newTransformer()
 	transformer.setOutputProperty(OutputKeys.INDENT, "yes")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gradle-plugin/src/main/java/com/itsaky/androidide/gradle/JdwpPlugin.kt`
around lines 100 - 105, Create a TransformerFactory instance instead of calling
TransformerFactory.newInstance() inline, enable secure processing on it by
calling setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true), then call
newTransformer() to get the Transformer used with DOMSource(document) and
StreamResult(outputFile); update the code around
TransformerFactory.newInstance()/transformer to use this factory variable
(referencing TransformerFactory, XMLConstants.FEATURE_SECURE_PROCESSING,
newTransformer(), transform()).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@gradle-plugin/src/main/java/com/itsaky/androidide/gradle/JdwpPlugin.kt`:
- Around line 20-45: Replace the order-dependent hasPlugin(APP_PLUGIN) check
with pluginManager.withPlugin(APP_PLUGIN) so the registration runs even if the
Android app plugin is applied later: move the extension look-up and
onDebuggableVariants registration into a pluginManager.withPlugin(APP_PLUGIN)
block and register the JdwpManifestTransformerTask (referencing
JdwpManifestTransformerTask, onDebuggableVariants, and
SingleArtifact.MERGED_MANIFEST) inside that block; also remove the trailing .let
{ } no-op after the apply method.

---

Nitpick comments:
In
`@app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.kt`:
- Around line 448-451: Change the default for the enableJdwp parameter in
getGradleExtraArgs from JdwpOptions.JDWP_ENABLED to false so JDWP is only
enabled when explicitly requested; update the function signature of
getGradleExtraArgs(enableJdwp: Boolean = false, enableLogSender: Boolean =
DevOpsPreferences.logsenderEnabled) and ensure any call sites that relied on the
implicit default pass the intended boolean explicitly (refer to
getGradleExtraArgs and the enableJdwp parameter).

In
`@gradle-plugin/src/main/java/com/itsaky/androidide/gradle/COTGSettingsPlugin.kt`:
- Around line 59-63: The chain building the map entry for isTestEnv uses
mavenLocalRepos.split(File.pathSeparatorChar).toList() but split already returns
a List<String>, so remove the redundant .toList() call in COTGSettingsPlugin
(the expression that references isTestEnv and mavenLocalRepos) — simply use
mavenLocalRepos.split(File.pathSeparatorChar).filter { it.isNotBlank() } to
produce the desired list.

In `@gradle-plugin/src/main/java/com/itsaky/androidide/gradle/JdwpPlugin.kt`:
- Around line 100-105: Create a TransformerFactory instance instead of calling
TransformerFactory.newInstance() inline, enable secure processing on it by
calling setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true), then call
newTransformer() to get the Transformer used with DOMSource(document) and
StreamResult(outputFile); update the code around
TransformerFactory.newInstance()/transformer to use this factory variable
(referencing TransformerFactory, XMLConstants.FEATURE_SECURE_PROCESSING,
newTransformer(), transform()).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5cec0421-e03f-4e35-8ffd-824ba1d1daf3

📥 Commits

Reviewing files that changed from the base of the PR and between f0a7a2e and dc3c382.

📒 Files selected for processing (5)
  • app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.kt
  • gradle-plugin/src/main/java/com/itsaky/androidide/gradle/COTGSettingsPlugin.kt
  • gradle-plugin/src/main/java/com/itsaky/androidide/gradle/JdwpPlugin.kt
  • gradle.properties
  • lsp/java/src/main/java/com/itsaky/androidide/lsp/java/JavaLanguageServer.kt

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.kt (1)

448-451: Consider requiring explicit enableJdwp at callsites.

Line 449’s default (JdwpOptions.JDWP_ENABLED) makes it easy for a future callsite to accidentally bypass debug-intent gating. Making enableJdwp required is safer.

♻️ Suggested refactor
-	private fun getGradleExtraArgs(
-		enableJdwp: Boolean = JdwpOptions.JDWP_ENABLED,
-		enableLogSender: Boolean = DevOpsPreferences.logsenderEnabled,
-	): List<String> {
+	private fun getGradleExtraArgs(
+		enableJdwp: Boolean,
+		enableLogSender: Boolean = DevOpsPreferences.logsenderEnabled,
+	): List<String> {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.kt`
around lines 448 - 451, The getGradleExtraArgs function currently defaults
enableJdwp to JdwpOptions.JDWP_ENABLED which can let callers unintentionally
enable/disable JDWP; change getGradleExtraArgs to make enableJdwp a required
parameter (remove the default), update all call sites of getGradleExtraArgs to
pass an explicit boolean (e.g., use JdwpOptions.JDWP_ENABLED or the appropriate
runtime flag), and ensure any related callers that relied on the default
(including tests) are adjusted so JDWP intent is always explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.kt`:
- Around line 356-363: The current JDWP gating derives isDebugBuild using
getBuildType(buildInfo.tasks) which only checks the first task; update the logic
so isDebugBuild is true when any task in buildInfo.tasks represents a debug
variant (e.g., tasks that end with "Debug" or explicit names like
"assembleDebug", "installDebug", "bundleDebug"), then pass that result into
getGradleExtraArgs(enableJdwp = JdwpOptions.JDWP_ENABLED && isDebugBuild).
Modify either getBuildType to examine all tasks or replace the single-task check
with a new predicate that scans buildInfo.tasks for debug-related task names so
JDWP is enabled for all valid debug task flows.

---

Nitpick comments:
In
`@app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.kt`:
- Around line 448-451: The getGradleExtraArgs function currently defaults
enableJdwp to JdwpOptions.JDWP_ENABLED which can let callers unintentionally
enable/disable JDWP; change getGradleExtraArgs to make enableJdwp a required
parameter (remove the default), update all call sites of getGradleExtraArgs to
pass an explicit boolean (e.g., use JdwpOptions.JDWP_ENABLED or the appropriate
runtime flag), and ensure any related callers that relied on the default
(including tests) are adjusted so JDWP intent is always explicit.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1939825b-9d0a-478f-8c96-4ee341048925

📥 Commits

Reviewing files that changed from the base of the PR and between dc3c382 and 7e3a1b5.

📒 Files selected for processing (1)
  • app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.kt

Comment on lines 356 to +363
val buildType = getBuildType(buildInfo.tasks)
val isDebugBuild = buildType == "debug"

val currentTuningConfig = tuningConfig
var newTuningConfig: GradleTuningConfig? = null
val extraArgs = getGradleExtraArgs()

@Suppress("SimplifyBooleanWithConstants")
val extraArgs = getGradleExtraArgs(enableJdwp = JdwpOptions.JDWP_ENABLED && isDebugBuild)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

JDWP debug gating can miss valid debug task flows.

Line 357 derives debug intent from getBuildType(...), which only inspects the first task and only maps assembleDebug to debug. If a debug session runs installDebug/bundleDebug (or debug task is not first), Line 363 will disable JDWP and skip the intended manifest permission path.

🔧 Proposed fix
-			val buildType = getBuildType(buildInfo.tasks)
-			val isDebugBuild = buildType == "debug"
+			val buildType = getBuildType(buildInfo.tasks)
+			val isDebugBuild =
+				buildInfo.tasks.any { task ->
+					task.contains("assembleDebug", ignoreCase = true) ||
+						task.contains("installDebug", ignoreCase = true) ||
+						task.contains("bundleDebug", ignoreCase = true)
+				}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.kt`
around lines 356 - 363, The current JDWP gating derives isDebugBuild using
getBuildType(buildInfo.tasks) which only checks the first task; update the logic
so isDebugBuild is true when any task in buildInfo.tasks represents a debug
variant (e.g., tasks that end with "Debug" or explicit names like
"assembleDebug", "installDebug", "bundleDebug"), then pass that result into
getGradleExtraArgs(enableJdwp = JdwpOptions.JDWP_ENABLED && isDebugBuild).
Modify either getBuildType to examine all tasks or replace the single-task check
with a new predicate that scans buildInfo.tasks for debug-related task names so
JDWP is enabled for all valid debug task flows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants