android – Run an instrument test from within app and wait for result-ThrowExceptions

Exception or error:

I am developing a QA automation solution that can record/playback QA tests on Android. A key business requirement is to have no dependency on a connected PC while playing back the recorded test. To that end, I’m trying to run an Instrumentation test without a connected PC. (Specifically, an Appium UiAutomator2 test).

My current approach is trying to run the test programmatically from my app. If I were running the test normally from a connected PC, I would use the command adb shell am instrument -w. I tried accessing ADB Shell from my app and running am instrument -w, but this produces an error that I am missing the INTERACT_ACROSS_USERS_FULL permission.

To get around this issue, I am trying to run the test using startInstrumentation. This successfully starts the test. However, the test immediately crashes. Upon further investigation, I traced the crash to an NPE: the test is trying to retrieve InstrumentationRegistry.getInstrumentation.getUiAutomation(0), but this returns null.

How do I run the test programatically and give it access to the required UiAutomation instance?

This is how I start the test:

public void runTest() {
    final String pm = getPackageName().replaceFirst(".test$", "");
    final InstrumentationInfo info = getInstrumentationInfo(pm);
    if (info != null) {

        final ComponentName cn = new ComponentName(info.packageName,
                info.name);

        Bundle arguments = new Bundle();
        arguments.putString("class", "io.testim.appiumwrapper.test.AppiumUiAutomator2Server");
        //cn = {io.extension.test/android.support.test.runner.AndroidJUnitRunner}
        startInstrumentation(cn, null, arguments);

    } 
}
How to solve:

see signature protection level – clarifying …unless having the package white-listed by Google’s release key, you won’t be able to obtain the necessary permission. this is a security/integrity feature, with the purpose to limit what malware is able to do – and what you intend to do there, is typical malware behavior – no matter the actual intention of it; working against the system leads nowhere.

the only way I could imaging would be to run commands directly from a terminal emulator or the test application – against a custom build of AOSP, so that you could add android:protectionLevel="signature" to the Manifest.xml and then require android.permission.INTERACT_ACROSS_USERS_FULL. but with a stock ROM, there definitely is no chance to do so. it is not, that it would be “impossible”, but building a custom ROM means quite some effort, in order to get there. at least for Nexus and Pixel devices, the required drivers are available here; for other devices, you’d have to find them at the device’s vendor, if they’re even available.

the trick is to sign off the ROM with the same key as the app – only then signature level permissions can be obtained – while for a stock ROM, you’d (theoretically) need Google’s release key to sign off the package. one can enforce single user, as explained here, while this also is only available to system apps.

Leave a Reply

Your email address will not be published. Required fields are marked *