javaandroidandroid-uiautomator

How to grant Android runtime & special permissions on GMD Android Test Device


The background

In 2021, Gradle Managed Devices (DMG), and Android Test Device (ATD) images were released. Using emulators with these images tends to be faster and use less resources for instrumentation tests.

The downside is there appears to be very little documentation about them.
I was unable to find anything talking about dealing with permissions & instrumentation tests on ATDs pretty much anywhere.

Which was a problem because I'm trying to get our app's instrumentation tests to work on these devices, to make running them in CICD easier.

The problem

After Android 10, dealing with permissions changed. Now there are install permissions, runtime permissions, and special permissions.
When running tests on a GMD, they can easily be new images with no preset configurations. Which means you have to grant permissions.

Special permissions were very challenging to get working.
Usually when requesting these permissions, you have to issue a special intent to have the user double-confirm the permission, such as Settings.ACTION_MANAGE_WRITE_SETTINGS.

This is a problem, because ATDs don't have Settings installed. Which means the app crashes, with this stack trace:

android.content.ActivityNotFoundException: No Activity found to handle Intent
    { act=android.settings.action.MANAGE_WRITE_SETTINGS
    cat=[android.intent.category.DEFAULT] dat=package:sequoia.gui.demo flg=0x50800000 }
    at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:2087)
    at android.app.Instrumentation.execStartActivity(Instrumentation.java:1747)
    at
    androidx.test.runner.MonitoringInstrumentation.execStartActivity(MonitoringInstrumentation.java:605)

I've tried GrantPermissionsRule:

Permission: android.permission.WRITE_SETTINGS cannot be granted!
failed: existence(sequoia.gui.test.app.MainActivityTest)
----- begin exception -----
junit.framework.AssertionFailedError: Failed to grant permissions, see logcat for details
    at junit.framework.Assert.fail(Assert.java:50)
    at
    androidx.test.runner.permission.PermissionRequester.requestPermissions(PermissionRequester.java:111)
    at
    androidx.test.rule.GrantPermissionRule$RequestPermissionStatement.evaluate(GrantPermissionRule.java:135)

And also UiAutomation.grantRuntimePermission:

java.lang.SecurityException: Error granting runtime permission
    at android.app.UiAutomation.grantRuntimePermissionAsUser(UiAutomation.java:1271)
    at android.app.UiAutomation.grantRuntimePermission(UiAutomation.java:1238)

How can this be achieved?


Solution

  • The solution I finally got to work was using the built-in command appops:

    Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
    UiDevice device = UiDevice.getInstance(instrumentation);
    
    String targetPackageName = instrumentation.getTargetContext().getPackageName();
    String permissionCommand = String.format("appops set %s WRITE_EXTERNAL_STORAGE allow", targetPackageName);
    
    device.executeShellCommand(permissionCommand);