adb devices is the most-typed adb command. The follow-on traps are real: scripts run before the device is actually online, multi-device shells fail with more than one device/emulator, Linux misses udev rules, and tutorials reference a fictional wait-for-device-online form. This skill encodes the truth set.
When to use this skill
- The user runs
adb shellwith two devices plugged in and getserror: more than one device/emulator. - The user reports a device showing as
unauthorized,offline, orno permissionsinadb devices. - The user wants a CI script to wait until a device finishes booting before running tests.
- The user copy-pasted
adb wait-for-device-onlinefrom a blog post and it errors with usage text. - The user is on Linux and a freshly plugged phone never appears in
adb devices.
When NOT to use this skill
- The user wants the high-level architecture / server lifecycle / env vars — use
../../architecture/understanding-adb-architecture/SKILL.md. - The user wants Wi-Fi / wireless debugging — use
../connecting-over-wifi/SKILL.md. - The user wants to install or clear app state on the connected device — use
../../apps/installing-and-managing-apps/SKILL.md.
Prerequisites
- Working ADB server (see
../../architecture/understanding-adb-architecture/SKILL.md). - Device with USB debugging enabled in
Settings → System → Developer options. On Android 4.2+ developer options is hidden until "Build number" is tapped seven times inAbout phone. - On Linux: ability to write
/etc/udev/rules.d/51-android.ruleswith sudo. - On Windows: a vendor USB driver (Google USB Driver via SDK Manager for Pixel/Nexus; OEM driver for other manufacturers).
Workflow
-
1. Run
adb devicesfirst, every time. It prints one line per attached transport in the form<serial> <state> [details...]. Add-lwhenever scripting:adb devices -l # List of devices attached # emulator-5556 device product:sdk_google_phone_x86_64 model:Android_SDK_built_for_x86_64 device:generic_x86_64 transport_id:1 # 0a388e93 device usb:1-1 product:razor model:Nexus_7 device:flo transport_id:2-laddsproduct,model,device,transport_id, and (for USB) the USB bus path. -
2. Pick the right selector for the situation:
Flag Meaning Fails when -s <serial>Explicit serial. Works for USB serials ( 0a388e93), emulators (emulator-5554), and TCP devices (192.168.1.42:5555). Overrides$ANDROID_SERIAL.Serial not connected. -dThe single USB device. Zero or more than one USB device. -eThe single TCP/IP device (covers emulators and adb connect-ed phones).Zero or more than one TCP device. -t <transport_id>Numeric transport ID from adb devices -l. Stable across re-plugs of the same port.Transport ID not present. Note:
-tis the transport ID, not a timeout flag. Wrap with shelltimeout(orgtimeouton macOS) if the goal is a per-command time limit. -
3. Filter to "ready" devices in scripts:
adb devices | awk '$2=="device"{print $1}'Single-device shortcuts:
adb get-serialnoreturns the serial,adb get-statereturns one ofoffline | bootloader | device,adb get-devpathreturns the USB path. For the full state set, parseadb devicesdirectly. -
4. Use the canonical
wait-for-*form. The variants you may have seen are wrong. Fromadb help(scriptingsection), the only valid syntax is:wait-for[-TRANSPORT]-STATE TRANSPORT ∈ { usb, local, any } (transport defaults to any) STATE ∈ { device, recovery, rescue, sideload, bootloader, disconnect }Common forms:
Command Blocks until... adb wait-for-deviceAny transport, state device. The default — used at the start of CI scripts.adb wait-for-usb-deviceA USB-attached device reaches state device.adb wait-for-local-deviceA TCP/IP device (emulator or wireless) reaches state device.adb wait-for-bootloaderDevice shows up in bootloader(fastboot) state.adb wait-for-recovery/wait-for-sideload/wait-for-rescueSame idea for those modes. adb wait-for-disconnectThe currently selected device disconnects (e.g. after adb reboot).The doc does NOT define a
wait-for-device-onlineform.wait-for-*only checks the transport, not boot completion — pair it with asys.boot_completedpoll:adb wait-for-device until [[ "$(adb shell getprop sys.boot_completed | tr -d '\r')" == "1" ]]; do sleep 1 done adb shell input keyevent 82 # unlock the AOSP slide-up screenCompound flow around a reboot:
adb shell reboot adb wait-for-disconnect adb wait-for-device -
5. Read device states correctly. From the adb page (
Query for devices) plusadb help:State Meaning Recovery deviceOnline, adbdis responsive. Does not imply boot complete — gate onsys.boot_completed.n/a offlineTransport exists but adbdisn't talking. Common after suspend/resume or USB hubs.adb reconnect offline, then re-plug if needed.unauthorizedHost RSA key not yet accepted on-device. Unlock device, tap Allow on the on-device dialog. Try a different USB cable if no dialog appears (some "charge-only" cables block data). recoveryBooted into recovery. Only a small subset of adbworks.n/a sideloadOTA sideload mode. Use adb sideload package.zip.n/a bootloaderAt the bootloader (fastboot). adbcannot talk; usefastboot.n/a rescueRescue Party mode (Android 10+). n/a connectingTransient TLS handshake state over wireless. wait. no permissions(Linux only) USB visible but blocked by missing udev rules. install rules — see step 7. -
6. Accept the RSA fingerprint dialog on first connect.
"When you connect a device running Android 4.2.2 (API level 17) or higher, the system shows a dialog asking whether to accept an RSA key that allows debugging through this computer." — developer.android.com/tools/adb Sequence:
adbdsees a new client offering an RSA public key.- The OS pops the "Allow USB debugging?" dialog showing the workstation's key fingerprint.
- Until the user taps Allow,
adb devicesreportsunauthorized. - After acceptance, the public key is appended to
/data/misc/adb/adb_keyson the device. Tick Always allow from this computer so future connections from the same~/.android/adbkeyskip the prompt.
Revoke an old workstation's permission via
Settings → Developer options → Revoke USB debugging authorizations. -
7. (Linux) install udev rules so a freshly plugged phone is not stuck at
no permissions. Canonical file:/etc/udev/rules.d/51-android.rules. Minimal example (one line per OEM):# Google SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", MODE="0660", GROUP="plugdev", TAG+="uaccess" # Samsung SUBSYSTEM=="usb", ATTR{idVendor}=="04e8", MODE="0660", GROUP="plugdev", TAG+="uaccess"Apply without reboot:
sudo udevadm control --reload-rules sudo udevadm trigger # unplug and re-plug the deviceMost distros also ship a comprehensive
android-udev/android-sdk-platform-tools-commonpackage that drops the same file in/lib/udev/rules.d/. -
8. (Windows) install the vendor USB driver. Pixel / Nexus / generic AOSP devices use the Google USB Driver from
SDK Manager → SDK Tools → Google USB Driver(files atandroid_sdk\extras\google\usb_driver\). Other OEMs require manufacturer drivers fromhttps://developer.android.com/tools/extras/oem-usb. macOS and Linux do not need vendor drivers. -
9. Multi-device fan-out. ADB has no built-in
--allflag. Two patterns:# Sequential for s in $(adb devices | awk '$2=="device"{print $1}'); do adb -s "$s" install -r app.apk done # Parallel (4 jobs at a time) adb devices | awk '$2=="device"{print $1}' \ | xargs -I{} -P 4 adb -s {} install -r app.apkFor sharded CI runners that each own one phone, lock the server to a single device:
adb --one-device 0a388e93 start-server
Patterns
Pattern: WRONG vs RIGHT — more than one device/emulator
# WRONG
adb shell getprop ro.product.model
# adb: more than one device/emulator
# WRONG because: with two transports attached (e.g. an emulator + a USB phone),
# every non-server adb command needs an explicit selector. The command did not
# fail; it never even ran.
# RIGHT
adb -s emulator-5554 shell getprop ro.product.model
# Or, if there is exactly one USB phone:
adb -d shell getprop ro.product.model
# Or set $ANDROID_SERIAL once and stop typing -s:
export ANDROID_SERIAL=emulator-5554
adb shell getprop ro.product.model
Pattern: WRONG vs RIGHT — fictional wait-for-device-online
# WRONG
adb wait-for-device-online
# error: usage: ...
# WRONG because: this command does not exist. The canonical syntax is
# wait-for[-TRANSPORT]-<state> with state in {device, recovery, rescue,
# sideload, bootloader, disconnect}. There is no -online state.
# RIGHT
adb wait-for-device # any transport, state=device
# Then poll boot complete (transport up != system booted):
until [[ "$(adb shell getprop sys.boot_completed | tr -d '\r')" == "1" ]]; do
sleep 1
done
Pattern: WRONG vs RIGHT — racing emulator startup
# WRONG
emulator -avd Pixel_API_34 &
adb install -r app.apk # races: emulator may not be online yet
# Often: error: device 'emulator-5554' not found
# RIGHT
adb start-server # important: see corner case below
emulator -avd Pixel_API_34 &
adb -s emulator-5554 wait-for-device
until [[ "$(adb -s emulator-5554 shell getprop sys.boot_completed | tr -d '\r')" == "1" ]]; do
sleep 1
done
adb -s emulator-5554 install -r app.apk
Corner case (verbatim from the adb page): "running emulators [may] not show up in
adb devices... when all of the following are true: the adb server is not running; you use theemulatorcommand with the-portor-portsoption with an odd-numbered port between 5554 and 5584; ... You start the adb server after you start the emulator." Workaround:adb start-serverBEFOREemulator.
Pattern: WRONG vs RIGHT — transport_id vs -t confusion
# WRONG
adb -t 60 shell getprop ro.product.model
# WRONG because: -t is transport_id, not a 60-second timeout. Either there is
# no transport with id=60 (error: device not found) or the command runs against
# the wrong device.
# RIGHT
# Real timeout via the shell tool:
timeout 60 adb -s emulator-5554 shell getprop ro.product.model # GNU timeout
gtimeout 60 adb -s emulator-5554 shell getprop ro.product.model # macOS (coreutils via brew)
# Use -t only with a transport_id from adb devices -l:
adb -t 2 shell getprop ro.product.model
Mandatory rules
- MUST add a selector (
-s/-d/-e/-t) on every adb command when more than one transport is attached, OR set$ANDROID_SERIAL. - MUST use the documented
wait-for[-TRANSPORT]-<state>syntax. There is nowait-for-device-online. - MUST poll
getprop sys.boot_completedafterwait-for-devicewhen the script needs the OS fully booted (e.g. before installing an APK or invokingam instrument). - MUST NOT confuse
-t TRANSPORT_IDwith a timeout flag. Wrap withtimeout/gtimeoutfor actual timeouts. - MUST NOT delete
~/.android/adbkey*to "fix"unauthorized— see../../architecture/understanding-adb-architecture/SKILL.mdfor the correct recovery. - PREFERRED: use
adb devices -landtransport_idfor scripts that may see two devices with the same serial (rare hardware bug or duplicate AVDs). - PREFERRED: install the distro's
android-udevpackage on Linux instead of hand-maintaining51-android.rules.
Verification
-
adb devices -llists every connected transport with statedevice(nooffline,unauthorized, orno permissions). -
adb devices | awk '$2=="device"{print $1}'returns one line per ready device. -
adb -s <serial> get-stateprintsdevice. -
adb wait-for-devicereturns immediately when at least one transport is in statedevice. -
adb shell getprop sys.boot_completedprints1after a fresh boot. -
adb -d shell echo ok(or-e, or-s) succeeds withoutmore than one device/emulator. - On Linux,
lsusbshows the device andadb devicesreportsdevice(notno permissions) after udev rules apply.
References
- ADB user guide (
adb devices,wait-for-*, RSA dialog): https://developer.android.com/tools/adb - Run-on-device setup (Linux udev rules and Windows drivers): https://developer.android.com/studio/run/device
- OEM USB drivers (Windows): https://developer.android.com/tools/extras/oem-usb
- AOSP
adbman page: https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/main/docs/user/adb.1.md tasks/research/A1-adb-architecture-devices.md— verbatimwait-for-*table, thewait-for-device-onlinecorrection, full state set, udev rule recipe.docs/CORPUS.md§I.3 (state truth set) and §I.10 (research findings).- Sibling skills:
- High-level architecture:
../../architecture/understanding-adb-architecture/SKILL.md - Wireless ADB:
../connecting-over-wifi/SKILL.md - Install / clear apps:
../../apps/installing-and-managing-apps/SKILL.md - Run instrumented tests:
../../tests/running-instrumented-tests-via-adb/SKILL.md
- High-level architecture:
- Cross-set neighbours:
- Run instrumented tests with
AndroidJUnit4:../../../instrumentation/runner/running-instrumented-tests-with-androidjunit4/SKILL.md - Configure JUnit4 on Android:
../../../jvm-tests/runner/configuring-junit4-on-android/SKILL.md - Source-set strategy:
../../../fundamentals/strategies/organizing-test-source-sets/SKILL.md
- Run instrumented tests with