Skip to main content

How An Android Widget Saved My Device UI Automation

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!

Comments

  1. 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.

    ReplyDelete
    Replies
    1. The 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.

      The 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.

      Delete

Post a Comment

Popular posts from this blog

Jenkins + Devices + AndroidJUnitRunner

New Android build system and test runner, same goose chase using undocumented features and hacks

As I've posted before, I am a big fan of Jenkins. It is extremely flexible, open source, and supported by a staggering array of plugins actively developed by engineers running over 100,000 instances of the server worldwide. With it's distributed node model, you can even build your own device cloud for hosting enterprise-scale automation, economizing hardware investments by sharing resources across multiple projects as well as speeding up automation by parallelizing test runs. I had been using a Jenkins-based system in the past to support instrumentation automation with Robotium quite happily. For the last couple years however, my work hasn't required that as much and I've found myself doing a lot more manual testing and using UiAutomator which didn't require a tight integration between the product codebase and the test code. As a result, I've been slow to adopt the…

UiAutomator and Watchers: Adding Async Robustness to UI Automation

"I'm looking over your shoulder... only because I've got your back." ~ Stephen Colbert
After my recent UiAutomator review a user brought up an important question about the use of UiWatcher. The watchers serve as async guardians of the test flow, making sure the odd dialog window doesn't completely frustrate your tests. Having a tool that automatically watches your back when you're focused on the functional flow of your tests is awesome. 100% pure awesomesauce. Since the API documentation on watchers is scant and the UI Testing tutorial on the Android dev guide doesn't cover their use in depth, I figured I should add a post here that goes over a simple scenario demonstrating how to use this fundamentally important UI automation tool.

In my example code below, I'm using uiautomator to launch the API Demo app (meaning run this against an Emulator built in API level 17 - I used the Galaxy Nexus image included in the latest ADT and platform tools). The te…

Run-As Like the Wind: Getting private app data off non-rooted devices using adb run-as and a debuggable app

"You're some kind of big, fat, smart-bug aren't you?"
~Johnny Rico, Starship Troopers (1997) One of the most important things about writing bugs is making them descriptive but concise. Screenshots, video, debug logging, and hardware snapshots are all fairly standard or available to Android testers these days and can save you enormously on text when communicating what constitutes the bug. Sometimes though, the app gets into a weird state due to some transient data issue where you may not own the API or the complexity with forcing the app data into a certain state is prohibitively high. In those cases it is very handy to directly inspect the data the app has in its own directories.

Getting at this data is trivial on emulators and rooted devices but due to file system permissions, this data is otherwise completely private to the app itself. If you're like me, you tend to test using devices rather than emulators and you probably prefer not to root your devices since t…