Skip to content

Update AGP and dependencies#19

Open
juangardi21 wants to merge 1 commit intomainfrom
task/ANDROID-17464-update-agp
Open

Update AGP and dependencies#19
juangardi21 wants to merge 1 commit intomainfrom
task/ANDROID-17464-update-agp

Conversation

@juangardi21
Copy link
Copy Markdown

@juangardi21 juangardi21 commented Mar 27, 2026

🎟️ Jira ticket

ANDROID-17464

🥅 What's the goal?

Migrate the Gradle plugin from deprecated AGP internal APIs to the modern stable APIs, ensuring compatibility with newer AGP versions.

🚧 How do we do it?

  • Replace TestedExtension with AndroidComponentsExtension + ApplicationAndroidComponentsExtension (new variant API) to resolve applicationId and adb path.
  • Collect applicationId per variant at configuration time using onVariants {} as a lazy Provider<String>.
  • Remove all ddmlib usage (IDevice, FileListingService, CollectingOutputReceiver) in DeviceFileManager and replace with direct adb subprocess calls via ProcessBuilder.

📘 Documentation changes?

  • No docs to update nor create

🧪 How can I test this?

Run the connected Android tests with and without record mode to verify snapshots are pulled and reports are generated correctly.

Compilation Tests running in SW
Grabacion.de.pantalla.2026-03-30.a.las.16.21.22.mov
Grabacion.de.pantalla.2026-03-30.a.las.16.23.33.mov

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
throw AndroidSnaptestingNoDeviceProviderInstrumentTestTasksException()
}

val extension = project.extensions.findByType(TestedExtension::class.java)
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

AGP 9.x removed TestedExtension from the public API. We now use the stable
variant API (AndroidComponentsExtension / ApplicationAndroidComponentsExtension) which is the recommended way to interact with Android build variants since AGP 7.x.

.firstOrNull { it.name == deviceProviderTask.variantName }
?: throw RuntimeException("TestVariant not found for ${deviceProviderTask.variantName}")
val applicationIdProvider = providerFactory.provider { testedVariant.applicationId }
val adbExecutablePath = extension.adbExecutable.absolutePath
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

adbExecutable was removed from TestedExtension in AGP 9.x.
sdkComponents.adb is the stable replacement: it returns a lazy Provider pointing to the adb binary resolved from the configured SDK.

it.iDevice.executeShellCommand("rm -rf ${getDeviceAndroidSnaptestingRootAbsolutePath()}", receiver)
println(receiver.output)
devices.forEach { device ->
runAdb(device.serialNumber, "shell", "rm", "-rf", getDeviceAndroidSnaptestingRootAbsolutePath())
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Previously used DDMLib's IDevice.executeShellCommand() to run 'rm -rf' on the device. Now delegates to runAdb(), which spawns an 'adb -s shell rm -rf ...' subprocess.


private fun getDeviceAndroidSnaptestingRootAbsolutePath(): String =
"${FileListingService.DIRECTORY_SDCARD}/Download/android-snaptesting/$applicationId"
"/sdcard/Download/android-snaptesting/$applicationId"
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

FileListingService.DIRECTORY_SDCARD was a DDMLib constant ("/sdcard"). Using the literal string directly removes the DDMLib dependency entirely.

destinationPath: String,
) {
val fileEntry = getDeviceAndroidSnaptestingSubfolderAbsolutePath(androidSnaptestingSubFolderInDevice).toFileEntry()
val remotePath = getDeviceAndroidSnaptestingSubfolderAbsolutePath(androidSnaptestingSubFolderInDevice)
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The old implementation built a FileEntry tree using DDMLib's FileListingService and then called IDevice.pullFile() for each entry.
The new implementation runs adb shell ls <remotePath> to list files and
adb pull <remote> <local> for each one, filtering out error lines from ls in case the folder doesn't exist yet on the device.

println(output)
}

private fun runAdbCapture(serial: String, vararg args: String): String {
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

runAdbCapture spawns a subprocess with the adb binary, targeting a specific device by serial (-s flag), merges stderr into stdout, and waits up to 60 seconds.

) {
device.fileListingService.getChildrenSync(androidSnaptestingDeviceFolder).forEach {
device.pullFile(it.fullPath, "$destinationPath/${it.name}")
private fun runAdb(serial: String, vararg args: String) {
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Convenience wrapper that additionally prints the output to the Gradle log. Both replace the DDMLib IDevice shell/pull APIs removed in AGP 9.x.

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.

1 participant