There are no clever quotes this time. Instead I'm just going to let you know that I've been in a high-fiving mood for hours now. Here's why...
Android Device UI Automation Limitations
In order to exercise the UI, the UI needs to be active. This means the screen needs to be awake and unlocked. All of the devices in my lab are required to be managed by the Google Apps Device Management Policy. This policy includes a maximum 30 minute screen timeout. Some devices retain a developer setting "Keep Screen On While Charging" despite the policy being in place. Some devices, especially the newer ones do not. It seems that the Device Policy overrides the developer settings and while I can see why that might be justified, it annoys me greatly. Since I do not have the privileges required to change the Device Policy, I have to work with it. Okay, more like *around* it.
Try, Try Again
I've tried many options to keeping screens unlocked. I've tried a variety of apps that supposedly handle this without consistent success. I've looked into what it might take to make my own app and set it up as a system app (hint: root the device, move the app to the /system/ directory with the other system apps, no thanks). I finally got so fed up that I took an app I was already working on for other purposes and decided to build a widget for it to take my mind off my problems. And then I saw it...
android:keepScreenOn="true"
Well holy crap. There it was on the widget's settings the whole time. Thankfully the system manages the widgets so I didn't have to write a service to keep an app active. Furthermore the widget doesn't need to be *doing* anything, just updating more rapidly than the screen timeout (in my case I have a 30 minute window). There is no interaction with the widget required, it just needs to have been added to one of the home screens. How awesome is that?!
This one is incredibly simple to pull off. Just add the following line to any view in your widget's main layout XML:
android:keepScreenOn="true"
Subversive Behavior
Okay, so I'm now using a widget to undermine both Device Policy AND the "proper" care and feeding of a device's normal battery etiquette. This is clearly a bit of a hack. The trick does NOT work if you press the power button. The trick does NOT work if any other app has focus rather than the home screens. The trick DOES work on all devices in my device lab though so as long as I can ensure that my fellow engineers clean up after themselves when it comes to CI and other test runs, my widgety shenanigans may just succeed. And even if Google were to fix this in future versions of Android, I'm fortunate to have already solved the whole "programmatically unlocking my screen before a test" thing. I know Google's Android team has an official stance regarding testing and that is to use emulators but I and my fellow QA Engineers believe that there's no better way to test your apps than on real devices. That's a genuine shout-out to my friends over at AppThwack who've worked up quite the fantastic platform for testing your apps across a myriad of real Android devices.
Happy testing!
Interesting. My approach would have been to have a watcher check for !getUiDevice().isScreenOn() and do something about that. For lower API levels and Robotium testing, I used to set a power manager wake lock in setup() and released it in tearDown(). That, however, depends on additional permissions.
ReplyDeleteThe watcher method is good and I'm using it for the tablets in the lab whose power draw is so overwhelming that keeping the screen on is just silly (Nexus 10, I'm looking at you and I'm not happy). I've actually packaged a "wake_me_up.jar" test to deploy to jelly bean devices and left it as a build step on test jobs on Jenkins. The test job pulls the Jar from a static repo, pushes it to the target device, runs that test first, and then does whatever real work the test job needs to do after that returns.
DeleteThe problems I had were definitely with lower levels and the fact that Google (rightly?) removed the ability for non-system apps to brutalize device batteries without running actual activities on the UI thread. I needed something that was both passively unobtrusive so my tests wouldn't need to account for it, and deeply embedded enough to where the permissions weren't a problem. On top of all that, putting it as a widget on an app that we also use for managing device inventory was handy since it could be used by testers and developers at their desks as well. It isn't a foolproof solution, for example in situations where tests do not fully clean up after themselves and exit to the home screen or uninstall their apps upon completion, the timeout and lock isn't blocked by the widget. Thankfully that can be solved by building more robust test jobs on Jenkins and so far my coworkers have been fairly consistent there. It really is cool to see a bunch of devices in the lab suddenly start running through the same scenarios on the same apps simultaneously.
Overall, I can say that mine is a somewhat unique scenario and clearly not what Google envisions. Some devices support the "Keep screen on while charging" setting but not all do and even those at the same SDK level vary on whether this setting is available. But platform fragmentation plus my need to consistently deploy the Google Apps Device Policy App left me in this awkward situation.