Starting and stopping android emulators
Android SDK provides ability to run code in emulated environment called emulator. Its quite easy to start using it. But when it’s time to setup your favorite Continuous Integration system, a lot of issues with emulators start popping up.
The most critical ones is how to start the emulator and detect when its ready to be used to run some code. And how to kill/stop emulators reliably.
In this post I’d like to share some ideas I’ve come across and that seems to work stable enough.
Starting an emulator
Usually emulator is started using emulator command-line tool. One required argument is AVD name. Usually this looks like:
A bunch of different arguments may be passed here, most of which are described in documentation, so I won’t go in details here.
Detecting correct emulator
Several emulators can run at the same time. And the first issue that I faced was how to detect serial number of emulator that was just started. For this I used device properties that can be set when emulator is started. In my case new UUID is generated for each emulator startup and then this UUID value is taken from all running emulators and correct emulator is then detected.
So, emulator is started with command line similar to:
emulator @android-test-avd-9 -prop emu.uuid=54dd0657-6c80-422a-b938-fb91db1b3cab
It then is possible to query properties from all emulators value of emu.uuid property. The one with correct uuid is selected.
Detecting when emulator is ready to be used
The second step is to wait for emulator to fully boot, so it can be used to run some code. This turned out to be the most tricky part. Especially for emulators with API Level 11+ (11-14 at the time of writing).
First, emulator should connect to adb and will be listed as “offline”. In this state emulator does not accept any shell commands. And nothing can be done in this state. This part is usually very stable and I haven’t seen the case when emulator was started but never connected to adb. Of course if some error occurs, it won’t connect to adb. So timeout should be used here to detect abnormal delays or hangs.
Next state device goes to called “device” state. This is when device is booting. As soon as it had booted, device goes to “online” state. This is when system starts booting and normal operation of emulator goes in.
From the moment device goes to “device” state, adb shell can be used to execute different commands on device and query some useful information about its state.
I’ve found several properties that should be tracked in order to reliably detect when device is ready. The first property is called dev.bootcompleted. As soon as device completes booting this property will be set to 1.
After dev.bootcompleted is 1, next property called sys.boot_completed should be tracked. It will be set to 1 as soon as system completed booting (this is usually when BOOT_COMPLETED broadcast is sent). This property is only set on emulators with API Level 9 or higher. On 8 and lower this property is never used (and shouldn’t be tracked).
But emulator is still not ready, even when sys.boot_completed is set to 1. You’ll notice that boot animation will still run for some (significant) period of time. And only then UI will appear. But luckily there is a way to detect this event too. For this we need to track value of init.svc.bootanim property. This property keeps state of boot animation service, that will be stopped as soon as UI appears. In other words, as soon as init.svc.bootanim has value stopped, its safe to assume that emulator is running and ready to be used.
Emulator disconnects from ADB
If you’d use emulators of API Level 11+, you’d notice that they almost always disconnect from adb just after sys.boot_completed is set to 1, and before init.svc.bootanim is set to stopped. From what I noticed, is that this happens just after ConnectivityManager resets all connections(for some reason). But you still need that emulator be connected to adb to be usable.
To reconnect device you’ll need to restart the adb. This should be done as soon as emulator is lost. And when adb is restarted the emulator should be listed as connected again. After that you should resume tracking emulator’s state to detect when it’s ready to be used.
I’ve also noticed that when 3+ emulators are running at the same time, this adb restart trick is working unreliably and fails quite often.
Starting the emulator: summary
1. Start the emulator with some property that has unique value (like new uuid).
2. Wait for emulator to connect to adb (I use 2 minutes timeout).
3. Wait for emulator to go to “device” state.
4. Wait for property "dev.bootcompleted” to be set to 1.
5. Wait for property “sys_bootcomplete” to be set to 1.
6. Wait for “init.svc.bootanim” to be set to “stopped”.
7. If emulator is no longer visible from adb, restart adb.
I usually use timeout of 5-10 minutes in total for steps 4-7.
Emulator should be stopped when all your code has run and you no longer need it. Another case is when you need to kill all running emulators to start with clean environment.
Killing emulator would’ve been quite easy, if not one bug in window’s version of emulator (linux version doesn’t have it).
Killing emulator with adb
The easy way to kill emulator is to run:
adb -s emulator-5554 emu kill
This command asks adb to connect to emulator console on port 5554 and issue command “kill”. You can try this yourself by using telnet client and connect to this port. Then you’ll see menu with available commands.
But there is one bug on windows emulator console. It ignores first command. And adb is only capable to send on command per connection. Hence there is no way to reliably kill emulator using adb.
Killing emulator with Emulator Console
The emulator console’s protocol is quite easy. If the command was successful, it will reply with a bunch of lines with `OK` or `OK: some message’ at the end. For example when sending “network status” I received:
Current network status: download speed: 0 bits/s (0.0 KB/s) upload speed: 0 bits/s (0.0 KB/s) minimum latency: 0 ms maximum latency: 0 ms OK
But if I send invalid command, I will see something like:
KO: unknown command, try 'help'
So my workaround was to use this emulator console directly via custom Java socket communication implementation. It sends first command (like help) and ignores the results. And then sends kill, thus asking emulator to kill itself.
Detecting if emulator is running
On useful side effect of having emulator console, is that if emulator is not connected to adb, its still possible to detect if it’s running or not. For this you’d only need to connect to specific port and see if that works (and maybe send a few commands).
Killing all running emulators
Sometimes its useful to kill all running emulators. One of the reasons is need for better performance or better stability (the more emulators are running the more unstable they become, at least from my experience).
All emulators have their console ports opened between ports 5554 and 5584, only on even ports (i.e. 5556, 5558, …, 5582, 5584). So the obvious way to kill all emulators, is to go through all even ports in this range and send kill to all of them (keeping in mind described bug with first ignored command).
The positive side of this approach is that its cross-platform, doesn’t require special rights to kill other processes and can kill emulators that are not even connected to adb (which there are might be several from time to time).
Killing emulators: summary
1. Use emulator console.
2. Console port number can be extracted from emulator serial number (which looks like emulator-5554 or emulator-5582).
3. Send first command that has no side effect (because emulator console will ignore first command on windows, while on linux it will happily execute it). This can be help command for example.
4. If you want to kill all running emulators, just send kill to all even ports from 5554 to 5584 ports.