Adam: a second birth to Android’s ddmlib

In this post, I’ll introduce you to adam: a kotlin-based replacement for developer-to-android-device interaction.

ADB server API

All of the interaction with the ADB server happens via a TCP/IP socket. There is little to no documentation on how this should be done so I’ll try to explain this in a bit more detail here. First, you need to open a socket.

General request flow

Let’s start with a simple example: executing a shell command ls for a device with a emulator-5554 serial number. All the communication happens with the ISO/IEC 8859–1 encoding.

  1. Read 4 bytes from the socket. If everything is ok, then they will equal OKAY . If that’s not the case then next 4 bytes will indicate the error message length in hex followed by the actual error message
  2. Write 0008shell:ls to indicate that we want to use the shell service and execute the ls command
  3. Repeat step 2
  4. Read the actual output of the command from the socket

Types of requests

Requests to the ADB server could be divided into two separate groups:

  1. Continuous flow request, e.g. reading logcat output or pushing a file to the device

Single return requests

Request target

We’ve used this request before in the form of 001Chost:transport:emulator-5554 . But what other values can we use here?

Executing a single return request

Every request that returns a single value should do the handshake and the implement the actual return of the value:

Shell-based requests

A lot of interaction with Android devices happens via the shell. Here is a partial list of commands that are commonly used:

  • Package install via pm install
  • Packages list via pm list
  • Package uninstall via pm uninstall

System requests

Other types of interactions are done using the services implemented in the ADB:

  • Port-forwarding forward:SRC:TRG

Continuous requests

Shell commands

Some of the commands will continuously run until either the command will finish the execution or the command is cancelled. Notable examples are:

  • Running tests with am instrument

Sync service

This is a file synchronization service, used to implement, for example:

  • Pulling files

Adam

So why the heck would anyone work on a rewrite of an official library?

Sub-optimal resources usage

As you’d expect from a test runner, most of the time, the runner is waiting for the tests to finish. This led us to the use of coroutines extensively throughout our codebase. Unfortunately, all of the ddmlib’s code is blocking. What’s worse is that most of its commands spawn a new thread for each request. When testing on hundreds of devices at the same time, this leads to large thread pools and uses a lot of RAM.

╔═══════════╦════════════════════╦═══════════════╗
║ ║ Threads ║ RAM ║
╠═══════════╬════════════════════╬═══════════════╣
║ Ddmlib ║ 31 ║ 815Mb ║
║ Adam ║ 28 ║ 436Mb ║
╚═══════════╩════════════════════╩═══════════════╝

Code is not tested properly

This is one of the biggest concerns I have for ddmlib. It’s a library that is used extensively in a lot of tools: IntelliJ, IntelliJ Android plugin, fork, spoon and more. Nevertheless, I would expect more tests and coverage even though testing so many edge cases with new Thread() calls would be quite hard:

Limitations of ADB server are propagated to the user of ddmlib

We love using simple and clear API. With ddmlib even though most of the communication happens via sockets, the library itself doesn’t do a good job of propagating the most critical feature of each socket interaction — the timeout handling. Some of the commands have the configurable timeout as a parameter for the method like getScreenshot while others do not have this luxury. Even when the timeout can be specified, the implementation only respects the timeout for some part of the request, in case of getScreenshot its only one read method. If the write method gets stuck then you’re out of luck. This behaviour makes the usage of these API’s very hard and cumbersome.

General adam usage example

To use adam, you will need:

  1. Coroutines support (inherited from adam as a dependency)

Links

https://github.com/Malinskiy/adam

Software engineer & IT conference speaker; Landscape photographer + occasional portraits; Music teacher: piano guitar violin; Bike traveller, gymkhana