Android Espresso, Wake up device before test. How to use a custom manifest for test?-ThrowExceptions

Exception or error:

I’ve been writing tests with androids new espresso framework and find that it works well. One annoying thing (not particular to espresso) is that I have to make sure my screen is awake and unlocked for the tests to run. I found a workaround (through various sources) however I am not sure the best way to integrate it.

So this is what I did, in my “Home” activity I have the following code:

Home.class:

public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        /************ Put this in a conditional for a test version ***********/
    KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
    KeyguardManager.KeyguardLock keyguardLock = km.newKeyguardLock("TAG");
    keyguardLock.disableKeyguard();
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
}

Also you need to add the following permissions:

<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>

So after doing this my tests now wake my phone up to run so I don’t have to stand guard and make sure that the screen doesn’t turn off right before the tests start.

I would rather not include those permissions though in my app obviously. I know with gradle it is possible to make different “flavors” that have their own android manifest which will merge into the main manifest. I was thinking of using that but I’d rather not add a flavor just for that reason given this is already using the test build type to run. It looks like from the android gradle documentation that you can’t create an AndroidManifest for the instrumentTest directory as it will be auto generated.

However I was wondering if there is another way to do it without creating a variant and then specifying that the tests should run that variant. Also I’m not sure of the exact syntax of all that and thought it would be nice just to have this information on the site for others as it seems to be scattered about.

Lastly, if anyone knows of a better way of solving the issue of waking the phone up for the tests I’d love to hear it as I’m not a big fan of this way I’m attempting.

How to solve:

I actually figured out a really easy way to handle this. Remove the keyguard and wakelock permissions from the main manifest and put them in src/debug/AndroidManifest.xml like so:

src/debug/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
    <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
</manifest>

When the app is built for debug the above permissions will merge into the main manifest. By default the build system uses the debug build for instrument tests so this works fine.

Then in my onCreate I put the code mentioned in the question:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (BuildConfig.DEBUG) {
        KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
        KeyguardManager.KeyguardLock keyguardLock = km.newKeyguardLock("TAG");
        keyguardLock.disableKeyguard();
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
    }
    ...
}

Now my phone can run tests without me waking them up by hand first and I didn’t have to add the above permissions to the release version of my app.

###

The easiest approach is to use adb command like below if you are running the tests from CI environment for example:

adb -s $DEVICE_ID shell input keyevent 82

This will unlock your device screen.

###

Now that KeyguardLock is deprecated, you can simply use:

    if (BuildConfig.DEBUG) {
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
    }

###

I’ve created the src/debug/AndroidManifest.xml file as Matt suggested and added to following code to the testCase:

   @Override
    public void setUp() throws Exception {
        super.setUp();
        // Espresso will not launch our activity for us, we must launch it via getActivity().
        Activity activity = getActivity();
        KeyguardManager km = (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
        KeyguardManager.KeyguardLock keyguardLock = km.newKeyguardLock("TAG");
        keyguardLock.disableKeyguard();
        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);

    }

###

Although you already have accepted Matt Wolve answer, I think that polluting your code with test boilerplate is not a good idea (I am aware that using Espresso there are some situations where you have to, like adding idle flags for custom IdlingResources). I would like to add another approach:

    @ClassRule
    public static ActivityTestRule<HomeActivity> mActivityRuleSetUp = new ActivityTestRule<>(
            HomeActivity.class);


    private static void wakeUpDevice(){
        if (BuildConfig.DEBUG){
            HomeActivity homeActivity = mActivityRuleSetUp.getActivity();

            KeyguardManager myKM = (KeyguardManager) homeActivity.getSystemService(HomeActivity.KEYGUARD_SERVICE);
            boolean isPhoneLocked = myKM.inKeyguardRestrictedInputMode();

            if (isPhoneLocked){
                homeActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                        | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
            }
        }
     }


    @BeforeClass
    public static void setUp(){
        wakeUpDevice();
    }

Hope it helps.

###

For testing device set Lock pattern to NONE in Settings->Security
Then use instance of UiDevice and call its wakeUp() method

This method simulates pressing the power button if the screen is OFF else it does nothing if the screen is already ON. If the screen was OFF and it just got turned ON, this method will insert a 500ms delay to allow the device time to wake up and accept input.

###

Another best way to wake up device before test.
Simply add ActivityLifecycleCallback in your setUp method.

public class Moduletest extends ActivityInstrumentationTestCase2<Your Activity>{

 protected void setUp(){
    super.setUp();

    ActivityLifecycleMonitorRegistry.getInstance().addLifecycleCallback(new ActivityLifecycleCallback() {
      @Override public void onActivityLifecycleChanged(Activity activity, Stage stage) {
        if (stage == Stage.PRE_ON_CREATE) {
          activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        }
      }
    });
  }
}

###

I have done it this way: First make two rules, one for the activity and one for UI Thread:

@Rule
public ActivityTestRule<Your Activity> mActivityRule =
        new ActivityTestRule<>(Your Activity.class, true, true);

@Rule
public UiThreadTestRule uiThreadTestRule = new UiThreadTestRule();

And then in my first test method made this:

   @Test
   @LargeTest
   public void CreateAndSaveTaskEntity() throws Throwable {

            uiThreadTestRule.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Your Activity activity = mActivityRule.getActivity();
            activity.getWindow()
                    .addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON |
                            WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
                            WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);

                }
            });

            //begin test cases

            ...
    }

Of course you have to add in AndroidManifest.xml the permissions:

<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>

Leave a Reply

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