Managing multiple Android devices over ADB: a practical reference
Managing multiple Android devices over ADB comes down to three habits: check what is connected with adb devices -l, address every command to one device with -s <serial> or ANDROID_SERIAL, and wrap repeated actions in a loop over all serials. This reference collects the commands, flags and fixes for that workflow.
Getting started with ADB
ADB (Android Debug Bridge) connects a host to the adbd daemon on each device through a local server. Multi-device commands are needed for testing one build on several models, debugging device-specific crashes and batch installs.
Prerequisites:
- Platform-Tools installed and on PATH
- USB debugging enabled
- The host’s RSA key authorized on the device
adb version # client and platform-tools version
adb start-server # start the server on port 5037
adb kill-server # stop it after updates or hangs
Listing and identifying devices
adb devices # serial and state of every device
adb devices -l # adds product, model, device and transport_id
The -l output helps tell identical phones apart in a long list.
| State | Meaning | Action |
| device | Connected and authorized | Ready for commands |
| unauthorized | RSA key not accepted | Unlock the phone and accept the prompt |
| offline | Connected, but adbd not responding | Replug or restart the server |
| no permissions | Host can’t access the USB device (Linux) | Add a udev rule and replug |
adb -d get-serialno # serial of the single USB device
adb -s <serial> shell getprop ro.product.model # model name
adb -s <serial> shell getprop ro.build.version.release # Android version
Model and Android version are useful labels when results from several phones go into one report.
Targeting a specific device
Target flags go before the subcommand:
adb -s R58M12ABCDE shell # by serial (USB, emulator or ip:port)
adb -d install app.apk # the only device on USB
adb -e logcat # the only emulator or TCP/IP device
adb -t 3 reboot # by transport_id from adb devices -l
-t helps when two low-cost devices report the same serial, such as 0123456789ABCDEF, because the transport ID is always unique.
For a session or script that works with one device, set the serial once:
export ANDROID_SERIAL=R58M12ABCDE
adb shell getprop ro.product.model # goes to R58M12ABCDE
unset ANDROID_SERIAL # back to default targeting
Common error:
adb: more than one device/emulator
ADB refuses to guess a target when several devices are attached and none is specified. The error often comes from a forgotten emulator still running. Add -s <serial>, export ANDROID_SERIAL, or use -d / -e when only one device of that type is connected. An explicit -s overrides ANDROID_SERIAL.
Running commands on all devices
Loop over every serial in the device state:
for s in $(adb devices | awk ‘NR>1 && $2==”device” {print $1}’); do
echo “== s”adb-s”s” shell getprop ro.product.model
done
awk skips the header line and ignores unauthorized or offline phones.
Run slow jobs in parallel with xargs -P:
adb devices | awk ‘NR>1 && $2==”device” {print $1}’ \
| xargs -P 4 -I{} adb -s {} install -r app-release.apk
-P 4 processes four devices at a time; -I{} inserts each serial.
Batch jobs for the loop body:
- Install a build: adb -s “$s” install -r app.apk
- Clear app data: adb -s “$s” shell pm clear com.example.app
- Take a screenshot: adb -s “s”exec-outscreencap-p>”s.png”
- Pull logs: adb -s “s”logcat-d>”s.log”
With xargs, wrap redirections in sh -c ‘…’ so each device writes its own file.
Wireless and network connections
TCP/IP mode, starting from a USB connection:
adb -s <serial> tcpip 5555 # restart adbd listening on TCP 5555
adb connect 192.168.1.42:5555 # connect over Wi-Fi
adb disconnect 192.168.1.42:5555 # drop one; plain adb disconnect drops all
tcpip mode stays active until the device reboots; after that, USB is needed again.
Android 11+ Wireless debugging needs no cable. Open Developer options → Wireless debugging → Pair device with pairing code:
adb pair 192.168.1.42:37099 # enter the six-digit code
adb connect 192.168.1.42:41235 # port shown on the Wireless debugging screen
Network serials look like 192.168.1.42:5555 and work with -s like any other serial.
| Method | Speed | Stability | Setup |
| USB | Fastest, no network overhead | Most stable | Cable and driver |
| Wi-Fi (tcpip) | Network-bound | Lost after reboot | USB cable once per boot |
| Pairing (adb pair) | Network-bound | Port changes on reconnect | Android 11+, one-time pairing |
Working with devices you don’t physically have
Emulators handle basic checks across API levels and appear as emulator-5554, but bugs tied to vendor skins, cameras or chipsets need real hardware. When a bug only reproduces on a model nobody on the team owns, renting a remote phone farm online gives access to real hardware without buying it. Such services keep physical handsets connected and let you control them from a browser, so you can install the build and reproduce the issue on the exact model.
When a remote host exposes a device’s ADB port on its localhost, forward it over SSH:
ssh -N -L 5555:localhost:5555 user@remote-host
adb connect localhost:5555
Security note: never expose port 5555 to the open internet. ADB in tcpip mode is unencrypted and grants shell access.
App and file management across devices
adb -s <serial> install -r app.apk
adb -s <serial> install-multiple base.apk split_config.arm64_v8a.apk
adb -s <serial> uninstall com.example.app
adb -s <serial> shell pm list packages | grep example
adb -s <serial> push config.json /sdcard/Download/
adb -s <serial> pull /sdcard/Download/report.txt ./
adb -s <serial> shell am start -n com.example.app/.MainActivity
adb -s <serial> shell am force-stop com.example.app
adb -s <serial> shell pm clear com.example.app
-r keeps app data on reinstall; install-multiple installs split APKs from an App Bundle in one session. push and pull copy files to and from shared storage. am start launches an activity by component name, force-stop kills the process, and pm clear resets the app to first launch.
Logs, screenshots and debugging
adb -s <serial> logcat -c # clear the buffer
adb -s <serial> logcat -s MyAppTag # one tag, silence the rest
adb -s <serial> logcat ‘*:E’ # errors and above
adb -s <serial> logcat MyAppTag:D ‘*:S’ # tag at debug level
adb -s <serial> exec-out screencap -p > screen.png
adb -s <serial> shell screenrecord /sdcard/demo.mp4 # Ctrl+C to stop, 3 min by default
adb -s <serial> pull /sdcard/demo.mp4
adb -s <serial> bugreport bugreport.zip
exec-out streams binary data unaltered, so the PNG isn’t corrupted. Clear logcat before reproducing a bug to keep the capture short.
Troubleshooting
| Problem | Cause | Fix |
| unauthorized | RSA key not accepted | Accept the prompt or revoke authorizations |
| offline | Stale connection | Replug, restart the server |
| Port 5037 in use | Second ADB server | Keep one Platform-Tools on PATH |
| Device not listed | Driver or charge-only cable | Install the driver, use a data cable |
Running adb devices -l before every batch job catches all four early.

