ADB Driver¶
jumpstarter-driver-adb carries the Android Debug Bridge (ADB)
protocol between a remote Android device and a Jumpstarter client. This enables existing tools in the Android ecosystem
such as Android Studio to connect to a remote Android device as if it was physically
connected via USB.
How it works¶
Android devices are declared in the exporter config, one driver instance per device, and plugged into the exporter over USB (or reachable at their own TCP address). Jumpstarter moves the ADB protocol to your machine; ADB does everything else.
DUT ──USB──▶ EXPORTER ──Jumpstarter tunnel──▶ CLIENT
(owns the USB (your own adb,
connection) Studio, tradefed…)
Installation¶
pip3 install --extra-index-url https://pkg.jumpstarter.dev/simple/ jumpstarter-driver-adb
For the optional Python ADB API:
pip3 install --extra-index-url https://pkg.jumpstarter.dev/simple/ "jumpstarter-driver-adb[python-api]"
Configuration¶
For example, a simple Android phone bench could be configured as follows:
export:
# An Android phone cabled directly to the exporter over USB
phone:
type: jumpstarter_driver_adb.driver.AdbDevice
config:
usb_port: "1-4.2" # the physical USB port the device is plugged into
# A device whose adbd already listens on TCP, for example a virtual Cuttlefish instance
virtual:
type: jumpstarter_driver_adb.driver.AdbDevice
config:
transport: tcp
address: "10.0.0.5:5555"
Finding a device’s ADB USB port¶
Linux¶
On a Linux device, if ADB is installed, you can run the following command:
$ adb devices -l
List of devices attached
HVA1234567 device usb:1-4.2 product:sdk model:Pixel device:generic
The usb: field is the value to put in usb_port. With or without the usb:
prefix, both are accepted. On Linux it is the kernel’s bus-port path (1-4.2), which is
stable for a given physical port, so it is the identity a bench wants.
macOS¶
On macOS, the USB device is reported as the IOKit location ID:
$ adb devices -l
List of devices attached
HVA1234567 device usb:538116096X product:sdk model:Pixel device:generic
Copy the usb: field verbatim, X suffix included — here usb_port is 538116096X.
The X is not a hex marker. adb formats this value as decimal followed by a literal X,
so do not convert it to hex. The number itself is macOS’s IOKit location ID, which
describes where the device sits in the USB tree rather than anything about the device:
it packs the controller and each hub port into one integer, so it plays the same role as
Linux’s 1-1.1.
That makes it stable for fixed wiring, but it is a path through your hubs — moving the
device to another port, or re-cabling a dock, changes it. It is also not readable at a
glance the way 1-4.2 is. Prefer Linux for production exporters and keep macOS for local
development.
AdbDevice parameters¶
Parameter |
Description |
Type |
Required |
Default |
|---|---|---|---|---|
transport |
|
str |
no |
|
usb_port |
usb: the bench USB port, as reported by |
str |
one of usb_port/serial |
— |
serial |
usb: an explicit ADB serial, for hardware with no usable USB devpath |
str |
one of usb_port/serial |
— |
address |
tcp: the device’s own adbd endpoint, |
str |
yes for tcp |
— |
adbd_port |
adbd’s TCP port on the device ( |
int |
no |
5555 |
adb_path |
Path to the ADB executable on the exporter |
str |
no |
|
connect_timeout |
Timeout (seconds) for adb commands |
float |
no |
30.0 |
server_port |
Which ADB server to use. By default, the adb server is automatically started and shared. |
int |
no |
15037 |
adopt_existing_server |
Use an ADB server already listening on |
bool |
no |
true |
Always prefer the specific usb_port over a device serial.
A serial identifies a specific device; the port identifies a USB port, which stays consistent even if the DUT is swapped out.
AdbServer parameters¶
Deprecated; prefer AdbDevice. Declare it to point your tooling at the exporter’s whole
ADB server, or to get the server-level CLI.
Parameter |
Description |
Type |
Required |
Default |
|---|---|---|---|---|
adb_path |
Path to the ADB executable on the exporter |
str |
no |
|
host |
Host address of the ADB server on the exporter |
str |
no |
|
port |
Port of the ADB server on the exporter |
int |
no |
15037 |
connect_timeout |
Timeout (seconds) for adb commands |
float |
no |
30.0 |
adopt_existing_server |
Use an ADB server already listening on |
bool |
no |
true |
Running the exporter in a container¶
An exporter in a rootless container needs access to one USB device, and only that one.
The obvious approach — --device /dev/bus/usb/001/017 — does not survive normal use. The
<dev> number is reassigned whenever the device re-enumerates, which a power cycle or a
replug does, and neither Podman nor Docker can add a device to a running container. adb
also walks /dev/bus/usb itself and ignores any path component that is not all digits, so
a friendly symlink does not help either.
Mount the whole USB tree instead, so re-enumeration is followed automatically, and use a
udev rule to hand one port to the container’s user. Values to substitute:
Placeholder |
Meaning |
Example |
|---|---|---|
|
the USB port, the same value as |
|
|
the user the container runs as |
|
|
host directory holding the exporter’s ADB key |
|
|
your exporter image |
— |
1. Give the port to your user. Create /etc/udev/rules.d/72-jumpstarter-adb.rules:
SUBSYSTEM=="usb", KERNEL=="<PORT>", ENV{DEVTYPE}=="usb_device", \
TAG-="uaccess", TAG-="seat", OWNER="<USER>", GROUP="root", MODE="0600"
2. Apply it, then unplug and replug the device — udev sets node permissions when a device appears, so the rule takes effect on the next enumeration:
$ sudo udevadm control --reload
3. Check that one port, and only that port, changed hands:
$ ls -l /dev/bus/usb/*/
crw------- 1 <USER> root 189, 31 ... 031 # your device
crw-rw-r-- 1 root root 189, 0 ... 001 # everything else, untouched
4. Run the container:
podman run -v /dev/bus/usb:/dev/bus/usb \
-v <KEYDIR>:/root/.android \
-v /etc/jumpstarter/exporter.yaml:/etc/jumpstarter/exporter.yaml:ro \
<IMAGE>
<KEYDIR> matters: every ADB server generates its own ~/.android/adbkey, and the device
trusts keys, not containers. Without a persistent directory you have to authorize the
debugging prompt again on every container restart.
5. Confirm the container sees exactly one device:
$ podman run --rm -v /dev/bus/usb:/dev/bus/usb <IMAGE> adb devices -l
List of devices attached
<serial> device usb:<PORT> ...
If it lists nothing, an ADB server on the host is usually holding the device, since only
one server can claim it. Stop that server, or point the driver at it with server_port.
Things that fail quietly¶
Pitfall |
What happens |
Fix |
|---|---|---|
The rule file sorts after |
systemd tags removable devices |
Name the file |
|
Restores all of the user’s groups, the opposite of what you want here |
Leave it off; grant access with |
|
Rootless Podman fails in |
Add ranges for the user in |
--device does work rootless — it is only a bind mount — but it pins the node number
rather than the port. The cgroup device rules that could express “this major:minor only”
need root.
Differences between distributions¶
The mechanism above is upstream udev and upstream Podman, so it is portable, but three things differ by host and are worth checking before blaming the driver:
Host |
What changes |
|---|---|
SELinux (Fedora, RHEL, CentOS Stream) |
The bind mount keeps its SELinux label, so the container gets |
Debian, Ubuntu, Raspberry Pi OS |
Ship |
No systemd-logind |
No |
Rule ordering is not distribution-specific: udev collects /etc/udev/rules.d,
/run/udev/rules.d and /usr/lib/udev/rules.d and sorts them lexicographically as one
set, regardless of directory. A 72- file in /etc therefore runs before
73-seat-late.rules in /usr/lib on any systemd host.
Two Podman details are easy to trip over. --group-add keep-groups only works with the
crun runtime and is silently unavailable under runc — one more reason to grant access
with OWNER= on the port instead. And --userns=keep-id is optional here: by default a
rootless container maps your user to root inside the container, which already owns the
bind-mounted node; use keep-id only if the image expects to run as a specific UID.
The usb_port value itself is per-host, since it describes your USB topology. Read it
from adb devices -l on the exporter rather than copying it between benches.
An ADB server already running on the exporter¶
An ADB server claims the USB devices it finds, and only one server can hold a given
device. A second server on the same port therefore sees an empty device list — and
adb start-server exits 0 either way, so a driver that started one would look healthy
and see nothing at all.
The driver’s behavior follows from that:
Situation |
What the driver does |
|---|---|
A server is already listening on |
Adopts it, and leaves it running at teardown |
Several |
They share the one server; sharing is keyed on the port, not on |
Two drivers name different |
They share it, with a warning: an |
Something that is not an ADB server holds the port |
Declines to adopt it, and warns. |
Teardown only undoes what the driver itself did, at the end of the lease that created it:
A forward or an
adb connectthat was already there is reused and left in place.adb forward --listcannot say who created a forward, so removing one could break another lease — or a person at the bench — with no error reported anywhere.A forward the driver did create is re-checked before removal, because
adb forward --removematches on the local port alone and ignores-s <serial>. Anotheradb forwardon that port silently rebinds it to a different device, so the driver removes it only while it still forwards this device.
Port assignment¶
The exporter runs its ADB server on a non-standard port (default 15037) so it cannot collide with the standard 5037. This is important because Android Studio starts and maintains its own server on the default port.
Exporter-side forward ports are not configured: each forward is created as
adb forward tcp:0, so the ADB server picks a free port and the driver adopts
whatever it chose. Nothing on the exporter has to be kept clear of a guessed range.
Upgrading from an earlier AdbServer¶
Breaking change: the j adb <command> passthrough has been removed. Any script or
CI job that ran j adb shell, j adb logcat, j adb install and so on needs updating —
see the table below. Nothing else about the CLI or the config is source-incompatible.
Exporter configs need no changes. adb_path, host, port (still 15037) and
connect_timeout keep their names, types and defaults. adopt_existing_server is new and
defaults to true.
Three behaviors changed, all in the safer direction:
Before |
Now |
|
|---|---|---|
A server is already listening on |
A second one was started. |
It is adopted |
Exporter teardown |
Always ran |
Kills only a server it started itself |
|
Logged, then ignored: the driver carried on as if it owned a running server, and later adb calls failed far from the cause |
Raises |
If a bench previously “worked” but reported an empty device list, that was this bug and it
is now fixed. The unconditional kill is gone deliberately and cannot be restored:
adopt_existing_server: false asks for a server of the driver’s own, but it still never
kills one it did not start.
The j adb <command> passthrough has been removed. Jumpstarter no longer wraps the adb
CLI; it gives you an address and you use your own adb:
Before |
Now |
|---|---|
|
unchanged |
|
|
pointing tools at the exporter’s whole server |
|
Drivers that embed AdbServer — such as the Cuttlefish and Android emulator drivers —
need no changes. start_server, kill_server, connect_device, disconnect_device,
list_devices and adb_env keep their signatures and their failure behavior. The only
difference to be aware of is that close() may now leave the server running.
Usage¶
Starting the exporter¶
The examples below assume the config above, whose export is named phone, so the client
commands are j phone .... Rename to taste; the CLI follows the export name.
Local Exporter¶
$ jmp shell --exporter-config ./exporter.yaml
$ j phone info
$ exit
Useful for bringing a new bench up, since there is nothing else to run.
Direct Connection¶
On a separate exporter host, run the exporter as a service and connect to it directly, with no controller.
On the machine with the device attached:
jmp run --exporter-config /etc/jumpstarter/exporter.yaml \
--tls-grpc-listener 0.0.0.0:8083 --tls-grpc-insecure
Then from your client device:
$ jmp shell --tls-grpc <exporter-host>:8083 --tls-grpc-insecure -- j phone connect
connected as 127.0.0.1:41000
Attach a device to an existing ADB server¶
On the Jumpstarter client, run j <name> connect to add the ADB device to your local ADB server. For example, to join Android Studio’s existing ADB server.
$ j phone connect
connected as 127.0.0.1:41000
Your ADB server now lists it; use it with: adb -s 127.0.0.1:41000 shell
Android Studio will list it too.
Press Ctrl+C to detach
Leave it running for as long as you want the device available. Then, in another
terminal, it is just regular adb:
adb shell
adb install app.apk
adb logcat
adb push local_file.txt /sdcard/
Because adb connect is additive, the device joins whatever your ADB server
already holds — your own emulator, another bench, a phone — and every Android tool
sees it with no configuration. You do not need to own your ADB server, and you do not
need one at all: adb connect starts one if none is running.
Just give me the address¶
connect is a convenience. If you would rather drive adb yourself, or point a tool
that takes a host:port at the device:
$ j phone serve
127.0.0.1:41000
Add it to your ADB server with: adb connect 127.0.0.1:41000
Press Ctrl+C to stop
No adb runs on your machine at all. This is the primitive the rest is built on.
Is my device there?¶
$ j phone info
transport: usb
adbd_port: 5555
selector: usb:1-4.2
serial: HVA1234567
present: yes
A declared device that is powered off reports present: no with the reason. That is
normal, not an error — the exporter starts fine with every DUT powered down, and the
device is picked up the moment its relay turns on.
Requirements and limits¶
The device’s
adbdmust listen on TCP (persist.adb.tcp.port, commonly 5555). A stock phone needsadb tcpip 5555first — note this restartsadbdand may drop the USB connection.The local address (
127.0.0.1:<port>) is assigned per session and is not stable across sessions. Anything that remembers a device by address (a saved Android Studio run target) needs re-selecting after reconnecting. Use-Pto pin the port if you need one address to stay put.Direct mode has no lease arbitration, so two clients connecting the same device will interfere. Use distributed mode for a shared fleet.
Power cycles and re-enumeration¶
Nothing to configure: the device’s serial and its forward are resolved fresh on every
connection. A relay power-cycle re-enumerates USB and can hand the device a different
ADB serial, and the old forward disappears with it — the driver notices, re-resolves
the declared usb_port to the new serial, and forwards again. No config edit, no
exporter restart.
Reconnect after the DUT is back up; the local address will generally be a new port.
Transports¶
|
|
|
|---|---|---|
The device is |
plugged into the exporter over USB |
listening on its own TCP address |
Identified by |
|
|
On the exporter |
|
|
Typical case |
a bench DUT on a relay |
AAOS head unit, networked or virtual device |
There is no serial/UART transport¶
adb has none, so neither does this driver. Confirmed in AOSP: adb.h defines only
kTransportUsb and kTransportLocal (where “local” means TCP), and connect_device()
coerces every address to tcp: — adb connect serial:/dev/ttyUSB0 fails with
bad port number '/dev/ttyUSB0'. The dev: and dev-raw: specs that appear in
adb help are forward targets executed inside adbd on the device, not host
transports. Device-side there is no adbd-over-UART property either;
ttyGS0/gadget-serial gives a serial console, not an adb transport.
To reach a serial-only DUT, get it onto TCP and use transport: tcp: either use its
console to enable adbd over TCP (setprop service.adb.tcp.port 5555; stop adbd; start adbd), or bridge the UART to a TCP port outside Jumpstarter. Be aware that a raw UART
gives adb no retransmission and no checksum, so hardware flow control is mandatory,
the line must not be shared with a kernel console or getty, and at 115200 baud you get
~11.5 KB/s — enough for a shell, not for push or bugreport.
Pointing your tools at the exporter’s ADB server¶
The opposite model to connect: instead of adding one device to your server, aim
your tools at the exporter’s server and see its devices instead of your own. Right for
CI, a headless runner, or a container. Requires a declared AdbServer.
$ j adb serve
exporter's ADB server at 127.0.0.1:54321
To use your own adb or other tools, run:
export ANDROID_ADB_SERVER_ADDRESS=127.0.0.1
export ANDROID_ADB_SERVER_PORT=54321
Press Ctrl+C to stop
This replaces your server rather than adding to it, which is exactly wrong when an IDE
is running — Android Studio owns 5037 and respawns its server there within ~3s of
being killed, so the port cannot reliably be taken over. Use connect in that case.
Integration with Android ecosystem tools¶
How this relates to Android’s own remote-device support¶
connect is deliberately the same shape as the remote-device flow Google documents, so
Android Studio needs no Jumpstarter-specific support:
Android’s wireless debugging has you run
adb tcpip 5555thenadb connect <ip>:5555, and the device then appears as ahost:portserial alongside your emulators.connectdoes exactly that, except thehost:portis a local tunnel endpoint rather than the device’s own IP — which is what makes it work when the device is on a bench network you cannot route to.Because the ADB server “manages connections to devices and handles commands from multiple
adbclients”, remote and local devices coexist and are addressed with-s <serial>(or$ANDROID_SERIAL) in the ordinary way. Nothing about a Jumpstarter-attached device is special to a client.
Two deliberate differences: there is no pairing (the tunnel exists only for the lease, so there is nothing to remember or revoke — lease lifetime is the security boundary), and the address is not stable across sessions.
Android Studio¶
Run j phone connect. The device appears in Studio’s device chooser with no
configuration: no adb.server.port, no environment variables, no restart. Leave the
command running for as long as you want the device available.
Trade Federation (tradefed)¶
tradefed discovers devices through the ADB server, so an attached device is visible to it with no extra setup:
j phone connect # leave running
tradefed.sh
# > list devices <-- shows the attached device
To give tradefed the exporter’s whole device list instead, use j adb serve and
export ANDROID_ADB_SERVER_PORT.
Python API¶
Drive a device programmatically with adbutils
against the endpoint, no CLI involved:
# Requires: pip install jumpstarter-driver-adb[python-api]
import adbutils
with client.phone.serve() as target:
host, port = target.rsplit(":", 1)
adb = adbutils.AdbClient(host=host, port=int(port))
print(adb.device().prop.model)
Connecting the exporter’s server to a networked device¶
For a device the exporter should adb connect to — a Cuttlefish instance, say —
AdbServer exposes connect_device / disconnect_device. The address is supplied by
the caller; this driver does not discover or scan for devices. A parent composite
driver that owns the device lifecycle is the intended user: the Cuttlefish driver
embeds an AdbServer child and connects to an address derived from its own config.
For a networked device you want in your ADB server, prefer an AdbDevice with
transport: tcp — it needs no parent driver.
CLI¶
Per-device (j <device>.adb ...)¶
Usage |
Description |
|---|---|
|
Add this device to your own ADB server; holds until Ctrl+C |
|
Serve the device’s adbd at a local address; holds until Ctrl+C |
|
Show the device’s transport, selector and whether it is present |
Options for connect and serve:
Option |
Description |
Default |
|---|---|---|
|
Local address to bind |
127.0.0.1 |
|
Local port to bind (0=auto) |
0 |
|
Path to your local adb ( |
adb |
Server-level (j adb ..., needs a declared AdbServer)¶
Usage |
Description |
|---|---|
|
List devices visible to the exporter’s ADB server |
|
Forward the exporter’s ADB server to a local port; holds until Ctrl+C |
Everything else is your own adb, run directly.
API Reference¶
Device driver¶
- class jumpstarter_driver_adb.driver.AdbDevice¶
One declared Android device, exposed as a stream of its adbd.
Declared rather than discovered, which is what makes it composable: a DUT is a composite of its power relay, its console and this, so leasing the DUT leases the right device. It also means a device that is currently powered off is still described — startup does not require it to be present.
Identity for a USB device is the bench port (
usb_port), not the device’s serial, so hardware can be swapped between benches without a config change. The serial is looked up from the port on every call, which is exactly what changes when a relay power-cycles the DUT and USB re-enumerates.- connect()¶
Stream this device’s adbd.
The client port-forwards this and runs
adb connectagainst the local end, which adds the device to whatever ADB server the client already uses.
- info() dict[str, str]¶
Describe this device: its transport, selector, and whether it is present.
Takes a reference to the shared server first, like every other path that runs adb. Skipping it does not avoid starting a server — the adb client silently starts one when nothing answers the port — it only means the driver does not know it caused that server, adopts it on the next acquire, and then leaves it running at teardown because adopted servers are never killed. Observed against real hardware: info() alone left an ADB server behind after close().
A parent driver that runs adb itself, rather than streaming the device to a client, can
also use these. They are in-process calls on the child driver object, not exported to
clients — the Cuttlefish driver polls sys.boot_completed with its own adb shell, and
needs the device in the shared server before any client connects.
- class jumpstarter_driver_adb.driver.AdbDevice(*, uuid: ~uuid.UUID = <factory>, labels: dict[str, str] = <factory>, children: dict[str, Driver] = <factory>, description: str | None = None, methods_description: dict[str, str] = <factory>, log_level: str = 'INFO')
One declared Android device, exposed as a stream of its adbd.
Declared rather than discovered, which is what makes it composable: a DUT is a composite of its power relay, its console and this, so leasing the DUT leases the right device. It also means a device that is currently powered off is still described — startup does not require it to be present.
Identity for a USB device is the bench port (
usb_port), not the device’s serial, so hardware can be swapped between benches without a config change. The serial is looked up from the port on every call, which is exactly what changes when a relay power-cycles the DUT and USB re-enumerates.- adb_env() dict[str, str]
Environment pointing adb at this device’s server, holding a reference to it.
Acquiring the server is the point, not a side effect: without it the caller’s first adb call finds nothing on the port and the adb client silently starts a server this driver does not know about. The next acquire would then adopt it, and adopted servers are never killed, so it would outlive the lease.
- disconnect() None
Drop this driver’s
adb connect, if it made one, without ending the lease.For a parent driver that power-cycles the device mid-lease. A connection that was already in the shared server when this driver found it is left alone, and a later ensure_reachable reconnects as needed.
- ensure_reachable() str
Make the device reachable now and return the
host:portadb can address it by.Endpoints are otherwise resolved per stream, which is what makes re-enumeration self-healing. A parent driver that wants the device in the shared server before any client connects — to poll for boot, say — calls this instead.
For
transport: tcpthis runs the validatedadb connect; forusbit resolves the serial and its forward. Safe to call repeatedly.
Server driver¶
Deprecated. Prefer AdbDevice, which exposes one declared device and works with an
ADB server you already run. AdbServer is retained for the cases AdbDevice cannot
cover — pointing tooling at the exporter’s whole server, and devices that cannot expose
adbd over TCP — and because the Cuttlefish and Android emulator drivers embed it.
- class jumpstarter_driver_adb.driver.AdbServer¶
An ADB server on the exporter, tunneled to the client.
Point client tooling at this server with
forward_adb/j adb serve: the client then sees the exporter’s devices instead of its own. That is exclusive — the client must own its ADB server — so for adding a single remote device to an ADB server the client already runs (Android Studio’s, say), declare anAdbDeviceinstead.Declaring this driver is optional.
AdbDeviceensures a server on its own, and both route through the same per-process registry, so an explicitly declared server is the one a co-located device adopts.- connect_device(device: str) str¶
Connect the exporter’s ADB server to a device by address (host:port).
Raises on failure or timeout so callers can react instead of silently receiving an error string.
- disconnect_device(device: str) str¶
Disconnect an ADB device by address (host:port).
Raises on failure or timeout so callers can react instead of silently receiving an error string.
- kill_server() int¶
Kill the ADB server on the exporter. Returns the port number.
Destructive and shared: the server owns the USB device claims of every driver pointed at this port, so any co-located AdbDevice loses its forwards. Killing it clears our ownership, so close() does not later kill a server that some other process started on the freed port.
- list_devices() str¶
List devices visible to the exporter’s ADB server.
Read live from the ADB server on every call. Bounded, since adb devices blocks forever if a non-ADB process holds the port.
- start_server() int¶
Ensure the ADB server on the exporter is up. Returns the port number.
Operates on the shared registry entry rather than a throwaway one, so ownership stays accurate: adb start-server is silent and exits 0 whether it started a server or found one, so it cannot tell us. We probe instead, and only claim ownership of a server we actually started — otherwise close() would kill a server adopted from another process.
Device client¶
- class jumpstarter_driver_adb.client.AdbDeviceClient¶
Client for one declared Android device on the exporter.
Exposes the device’s adbd as a local TCP endpoint. What you do with that endpoint is up to your own adb:
connectruns a singleadb connectfor convenience, andservejust holds the address open so you can drive adb yourself.- connect(*, adb: str = 'adb', host: str = '127.0.0.1', port: int = 0, timeout: float = 60.0) Generator[str, None, None]¶
Add this device to the ADB server your machine already uses.
Three steps, none of them clever: the exporter streams the device’s adbd, Jumpstarter tunnels it here, and plain
adb connectadds it to the local server. Becauseadb connectis additive, the device lands in the default server — the one Android Studio, tradefed, gradle and a bareadball talk to — with no environment variables and no IDE restart. If you have no ADB server at all,adb connectstarts one.- Args:
adb: path to your local adb binary. host: local bind address. port: local port to bind; 0 lets the OS choose. The device’s address is whatever this resolves to — deliberately not something this driver invents, since ADB owns device addressing. timeout: seconds to allow the local
adb connect. A client-side timeout, distinct from the exporter’sconnect_timeout, because it bounds a command on your machine against a local port-forward.- Yields:
The
host:portthe device was connected as.
- info() dict¶
Describe the device: transport, selector, and whether it is present.
- serve(host: str = '127.0.0.1', port: int = 0) Generator[str, None, None]¶
Expose the device’s adbd on a local TCP port.
No adb is involved. This is the primitive: Jumpstarter moves the ADB protocol between the two machines, and your own tooling does the rest.
- Args:
host: local bind address. port: local port; 0 lets the OS choose.
- Yields:
The
host:portthe device’s adbd is reachable at.
Server client¶
- class jumpstarter_driver_adb.client.AdbClient¶
Client for the exporter’s ADB server.
Use this to point your own tooling at the exporter’s ADB server, which is exclusive — you see the exporter’s devices instead of your own. To add a single remote device to an ADB server you already run, use an
AdbDeviceand itsattachinstead.- connect_device(device: str) str¶
Connect the exporter’s ADB server to a device by address (host:port).
- devices() list[str]¶
Return the serials of usable devices on the exporter.
- disconnect_device(device: str) str¶
Disconnect an ADB device by address (host:port).
- forward_adb(host: str = '127.0.0.1', port: int = 0) Generator[tuple[str, int], None, None]¶
Forward the exporter’s ADB server to a local TCP port.
- Args:
host: Local bind address (default: 127.0.0.1) port: Local port (default: 0 = auto-assign, use 5037 to replace local ADB)
- Yields:
Tuple of (host, port) of the local listener.
- list_devices() str¶
List devices visible to the exporter’s ADB server.