adbutils and the case for a pure Python client for the adb server
pure python adb library for google adb service.
At a glance
- What is it?
- adbutils is a Python library that speaks to the Google adb service directly, giving you device objects, port forwarding, raw socket connections and shell execution without shelling out to the adb binary. It is a client of the adb server rather than a replacement for it, which is the boundary an adopter needs to understand first.
- Who is it for?
- Adopt adbutils if you are writing automation around Android devices, a test harness, a device farm or a UI tool, and you would rather work with typed objects and return codes than parse the output of the adb command. Do not adopt it as a replacement for the adb binary, because it is a client of the adb server on port 5037 and that server still has to be there.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 9 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A client for the adb server, not a replacement for adb
adbutils is described as a pure Python adb library for the Google adb service, and the distinction in that phrase is the first thing to understand. It does not implement the adb daemon. It connects to the adb server, the process that normally listens on port 5037, and speaks its protocol over a socket.
That design has two consequences, one good and one worth stating plainly. The good one is that everything happens in process, with structured return values and exceptions, rather than through a subprocess whose output has to be parsed as text. The other is that the adb server still has to exist and be reachable, whether it is the one the platform tooling starts for you or one you start yourself.
The library is aimed at developers and testers who need to script against Android devices. The topics in the repository make that clear, and so does the feature set, which covers everything from listing devices to port forwarding, raw sockets on the device, shell command execution, screenshots and monitoring device connection events.
The requirements are Python 3.8 or newer, and the licence is MIT. The README carries a table of contents that doubles as a map of the API, and the documentation includes pages for connecting to the adb server, listing devices, connecting remote devices, forwarding and reversing, creating socket connections, running shell commands, transferring files, extended functions, and command line usage.
For an adopter, the question of where the library sits in your stack has a simple answer. It is the layer between your automation and the adb server, and everything it does is expressed as a method on a client or a device object.
The adb server is addressed by host and port
The entry point is the AdbClient, and the connection parameters are the ones the adb server uses by default.
pip3 install adbutilsimport adbutils
adb = adbutils.AdbClient(host="127.0.0.1", port=5037)
for info in adb.list(extended=True):
print(info.serial, info.state)
print(info.transport_id)The extended listing is the interesting call, because it returns the transport id of each device alongside its serial and state. The state is the familiar device or offline, and the transport id is the number the adb command line shows with the long listing form, and it is what you use to pick a specific device when more than one is connected.
There is a shorter form for the common case, where device_list() returns only the devices whose state is device, and a socket timeout option on the client, since the default is None, which means a call can block indefinitely if the server is wedged.
The library also offers a convenience import, from adbutils import adb, which gives you a ready made client for the local server. The README uses that shortcut only in the first examples and then drops it, and the sections that follow are written without it.
Device objects, and the error when two devices are attached
Once the client is connected, the API is a set of device objects rather than a set of command strings.
A device is obtained by serial, or by transport id, and the transport id can be found with the long form of the adb devices listing. There is also a bare call, device(), which does not require a serial, and the README is explicit about the condition: you do not need to offer a serial if only one device is connected, and a RuntimeError will be raised if multiple devices are connected. That is a deliberate design choice, because silently picking one of two attached devices is the kind of default that produces a test run against the wrong phone.
The remote device operations mirror the command line exactly, which is the pattern for the whole library. Connecting and disconnecting are the same as the adb connect and adb disconnect commands, and the library adds the error handling the command line lacks, with a timeout parameter on connect and distinct exception types, AdbTimeout for a connection that does not answer in time and AdbError for other failures. A disconnect can be asked to raise rather than ignore a missing device.
There is also a wait helper, wait_for with a state, which blocks until a device reaches the state you want, whether that is device for the device to come online or disconnect for it to go away. That is a small convenience that removes one of the more common sources of flaky test code, the sleep before checking whether a device has finished rebooting.
Forward and reverse are the two halves of the plumbing
Port forwarding and port reversing are supported in full, and the README lists the command line equivalents so the mapping is obvious: forward and reverse with their remove and remove-all variants, and the list forms.
The device object exposes the forward side with a local address and a remote address, and the example uses the localabstract namespace, which is how the scrcpy server is usually reached. A convenience wrapper, forward_port, asks for a local abstract endpoint and returns a randomly assigned local port, which saves the caller from allocating one. The removal functions mirror that, forward_remove for one local address and forward_remove_all for everything tied to that device.
The reverse side is the same shape with the arguments the other way round, and the listing functions, forward_list and reverse_list, return items with a serial, a local and a remote address, and both can be filtered by serial when several devices are attached.
The last piece is event monitoring, track_devices, a generator that yields events with a present flag, a serial and a status. The README shows the output when two devices are plugged in, and notes that when the adb server is killed an AdbError is raised, which is the behaviour you want from a monitoring loop rather than a silent hang.
The namespace choice is worth a word. Using localabstract rather than a TCP port for the remote side is the pattern that lets a device-side service be reached without opening a port on the device, and it is why the same forward can work on a device connected over USB and on one connected over the network.
Raw sockets, and where the protocol contract comes from
The most powerful part of the library is create_connection, which opens a socket to a service on the device. The example given is minitouch, a touch injection daemon, which is a Unix domain socket on the device, and the other form is a TCP port such as 7912.
c = d.create_connection("unix", "minitouch")
print(c.recv(500))
c.close()c = d.create_connection("tcp", 7912)
c.send(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
print(c.recv(500))
c.close()There is a third form that reads a file on the device, using the device network constant and a path, which is how a file that a previous command wrote is read back.
The README points at SERVICES.TXT in the Android platform source for the list of services, which is the honest answer to the question of what else you can reach this way. The adb service catalogue is the contract, and anything in it can be opened as a socket.
This is what turns the library from a wrapper into a toolkit. A test harness that needs to inject touch events, read a log file or talk to a device-side service is doing three things that the adb command line makes awkward, and here they are three method calls on a device object.
The remaining operations in the library, transferring files and the extended functions, follow the same shape, and the command line usage section covers running the same capabilities from a terminal with the adbutils command, with environment variables for the connection and a colour logcat option.
shell, shell2 and the difference the v2 protocol makes
Shell execution has three forms, and the difference between them is about how much of the protocol you use.
The first is shell, which takes either a list of arguments or a single string, and a timeout. A timeout that expires raises AdbTimeout, which is the difference from a command line where a hung shell just hangs.
The second is open_shell, which returns a connection rather than a result, so you can send commands interactively and read the output as it arrives. That is what you want when the command produces output over time rather than at the end.
The third is shell2, which returns a structured ShellReturn with a return code and the captured output. The example shows the difference clearly, since a plain shell returns the output while shell2 gives the return code alongside the output, and the advanced form with the v2 flag uses the shell v2 protocol, which separates the streams, so a command that writes to standard error no longer has its error text mixed into standard output.
ret = d.shell2("echo 1; echo 2 1>&2", v2=True)Around that sit the small conveniences that make a test script readable. Properties are exposed as attributes, d.prop.name and d.prop.model, with a getter that can use cached data when the value has already been fetched, and the serial number, device path and state are available through methods that are named after their command line equivalents.
Screenshots come back as a Pillow image, and the requirements file confirms that Pillow is a hard dependency, which is the one heavyweight in a library that otherwise needs only requests, deprecation and retry2.
MIT, a typed package and a 2.x line on PyPI
The licence is MIT, with the text in the LICENSE file at the root of the repository, and the package is published on PyPI as adbutils.
The packaging is worth a note, because it shows how the project has aged. The setup.py declares setup_requires with pbr and sets pbr=True, which means the version and the metadata are derived from the repository rather than written by hand, and it ships a py.typed marker in the package data, which is what makes the type information usable by a type checker in a consuming project. The Python floor is 3.8.
There is one extra, and it is optional. The all extra installs apkutils, which is what you need for APK installation, and it is pinned to a version range so a major release of that companion library cannot break your install. The requirements file lists requests, deprecation, retry2 and Pillow, with apkutils commented in as an optional dependency required for APK installation, so the base install stays small.
The release history is regular. The recent versions are 2.12.0 on 2025-11-12, 2.11.0 on 2025-10-30 and 2.10.2 on 2025-09-08, so minor versions every few weeks with patch releases in between, on the 2.x line. The repository is not archived and the last push was on 2026-10-13, so development continues between those tags.
The repository layout matches a library with a real test suite, with the package in adbutils/, unit tests in tests/, end to end tests in e2etests/, examples including a script that resets an offline device, documentation in docs/, a pytest.ini and a coverage configuration.
The judgement for an adopter is that this is a mature, typed, small dependency library for the one job of driving Android devices from Python, and that its two constraints, the adb server has to be there and the protocol surface varies by Android version, are the things to test first.
Editorial conclusion
Adopt adbutils if you are writing automation around Android devices, a test harness, a device farm or a UI tool, and you would rather work with typed objects and return codes than parse the output of the adb command. Do not adopt it as a replacement for the adb binary, because it is a client of the adb server on port 5037 and that server still has to be there. Verify three things first: that a device is visible to the adb server before you write any code against it, that you pick the device explicitly when more than one is attached, since a bare device() raises RuntimeError, and that the operations you need are implemented for your Android version, because the protocol surface varies between releases. The last push was on 2026-10-13 and the newest release is 2.12.0 from 2025-11-12.
Frequently asked questions
What is adbutils?
A pure Python library for the Google adb service, published on PyPI as adbutils and licensed under MIT, requiring Python 3.8 or newer. It gives you device objects, port forwarding, raw socket connections to device services, shell execution and connection tracking instead of parsing command output.
How do I install adbutils and its optional parts?
Run pip3 install adbutils, or pip3 install adbutils[all] for the extra that pulls in apkutils, which is what is needed for APK installation. The base dependencies are requests, deprecation, retry2 and Pillow, and the package ships a py.typed marker.
How do I connect to a device with adbutils?
Create an AdbClient with a host and port, 127.0.0.1 and 5037 for the local adb server, then list devices with list(extended=True) or device_list(). A device is fetched by serial or by transport id, and the bare device() call raises a RuntimeError when more than one device is connected.
What can adbutils do with ports?
It covers both directions of the adb plumbing, with forward and reverse, forward_port for a randomly assigned local port, the remove and remove-all variants, and forward_list and reverse_list for inspection. It also offers track_devices, a generator of connection events, which raises AdbError if the adb server is killed.
How do I run commands and read files on a device with adbutils?
d.shell runs a command with an optional timeout and raises AdbTimeout when it expires, d.open_shell returns a connection for interactive use, and d.shell2 returns a structured result with a return code, where the v2 flag selects the shell v2 protocol that keeps standard error separate. d.create_connection opens a socket to a device service such as minitouch, over a unix socket or a TCP port.
How current is adbutils?
The newest release is 2.12.0 from 2025-11-12, preceded by 2.11.0 on 2025-10-30 and 2.10.2 on 2025-09-08, and the last push was on 2026-10-13. The library remains on the 2.x line, and it is a client of the adb server rather than a replacement for it.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/openatx-adbutils)Was this useful?