Keep every USB-plugged Android phone connected over Wi-Fi adb too (idempotent: never re-runs tcpip on a connected phone)
Keep your Android phone connected without the cable
When you test apps on an Android phone, your computer normally talks to the phone through a USB cable. The phone can also talk to the computer over Wi-Fi. Then you can unplug it, walk around with it, or charge it somewhere else, and the computer still reaches it.
Setting that up by hand takes a few steps every time, and it stops working whenever the phone restarts. This script does it for you.
What it does
When you run it, the script:
- Finds every phone that's plugged into your computer by cable.
- Looks up each phone's Wi-Fi address. If a phone isn't on Wi-Fi, it tells you to turn Wi-Fi on.
- Leaves a phone alone if it's already connected over Wi-Fi. That way it never interrupts something that's running on the phone.
- Connects the others over Wi-Fi. If a phone's Wi-Fi address changed since last time, it forgets the old one.
When it's done, you can unplug the cable and keep working.
Before you start
- A computer with a Terminal, such as a Mac, or Linux.
- Google's free Android tools for your computer, called "platform-tools", installed. On a Mac with Homebrew:
brew install android-platform-tools - On the phone: Developer options turned on, and USB debugging allowed in them. The first time you plug in, the phone asks whether to trust this computer: say yes.
- The phone and the computer on the same Wi-Fi network.
How to use it
- Download
adb-wireless.shfrom this page, and put it in a folder you can find, for example your home folder. - Open the Terminal and go to that folder.
- Allow the file to run. You only need to do this once:
chmod +x adb-wireless.sh - Plug in your phone with the cable, and run:
It tells you, phone by phone, what it did, for example:./adb-wireless.shadb-wireless: Pixel 8 is on Wi-Fi adb at 192.168.1.20:5555. - Unplug the cable. The phone stays connected.
Two other ways to run it
- Just see what it would do, without changing anything:
./adb-wireless.sh --dry-run - Keep checking every 30 seconds, so a phone that restarts is connected again the next time you plug it in:
Press Ctrl and C together to stop it../adb-wireless.sh --watch
If something doesn't work
- "No phone plugged in by USB": check the cable, and that the phone said yes to trusting the computer.
- "Has no Wi-Fi address": turn on the phone's Wi-Fi.
- "Couldn't connect": the phone and the computer are probably on different Wi-Fi networks.
- After the phone restarts, plug it in and run the script again.
| #!/usr/bin/env bash | |
| # Local only (listed in .git/info/exclude, never committed): makes sure every phone plugged in by USB is also | |
| # connected over Wi-Fi adb, on port 5555. | |
| # | |
| # scripts/local/adb-wireless.sh # once | |
| # scripts/local/adb-wireless.sh --dry-run # only say what it would do | |
| # scripts/local/adb-wireless.sh --watch # again every 30 s (after a reboot or a new IP), until Ctrl-C | |
| # | |
| # A phone already connected over Wi-Fi is left alone: `adb tcpip` restarts the phone's adbd and would cut a running | |
| # Maestro or Gradle session. | |
| set -uo pipefail | |
| PORT=5555 | |
| dry_run=false | |
| watch=false | |
| for arg in "$@"; do | |
| case "$arg" in | |
| --dry-run) dry_run=true ;; | |
| --watch) watch=true ;; | |
| *) echo "usage: $0 [--dry-run] [--watch]" >&2; exit 2 ;; | |
| esac | |
| done | |
| run() { if $dry_run; then echo " would run: $*"; else "$@"; fi; } | |
| # The phone's serial number behind an adb serial (USB or ip:port). | |
| serialno() { adb -s "$1" shell getprop ro.serialno 2>/dev/null | tr -d '\r'; } | |
| ensure_once() { | |
| local usb_serials | |
| usb_serials="$(adb devices -l | awk '$2 == "device" && / usb:/ {print $1}')" | |
| if [ -z "$usb_serials" ]; then | |
| echo "adb-wireless: no phone plugged in by USB" | |
| return 0 | |
| fi | |
| # The Wi-Fi connections adb has now, with the phone behind each. | |
| local tcp_serials | |
| tcp_serials="$(adb devices | awk '$2 == "device" && $1 ~ /:[0-9]+$/ {print $1}')" | |
| for usb in $usb_serials; do | |
| local model ip target sn | |
| model="$(adb -s "$usb" shell getprop ro.product.model 2>/dev/null | tr -d '\r')" | |
| sn="$(serialno "$usb")" | |
| ip="$(adb -s "$usb" shell ip -f inet addr show wlan0 2>/dev/null | awk '/inet /{print $2}' | cut -d/ -f1)" | |
| if [ -z "$ip" ]; then | |
| echo "adb-wireless: $model ($usb) has no Wi-Fi address: turn its Wi-Fi on" | |
| continue | |
| fi | |
| target="$ip:$PORT" | |
| local connected=false | |
| for tcp in $tcp_serials; do | |
| [ "$(serialno "$tcp")" = "$sn" ] || continue | |
| if [ "$tcp" = "$target" ]; then | |
| connected=true | |
| else | |
| echo "adb-wireless: $model's old address $tcp is stale (now $target), disconnecting it" | |
| run adb disconnect "$tcp" >/dev/null | |
| fi | |
| done | |
| if $connected; then | |
| echo "adb-wireless: $model is on Wi-Fi adb at $target" | |
| continue | |
| fi | |
| echo "adb-wireless: $model ($usb): switching to TCP mode and connecting to $target" | |
| run adb -s "$usb" tcpip "$PORT" >/dev/null | |
| $dry_run || sleep 2 | |
| run adb -s "$usb" wait-for-device | |
| local attempt | |
| for attempt in 1 2 3 4 5; do | |
| $dry_run && { run adb connect "$target"; break; } | |
| adb connect "$target" >/dev/null 2>&1 | |
| [ "$(adb -s "$target" get-state 2>/dev/null)" = device ] && break | |
| sleep 1 | |
| done | |
| if $dry_run || [ "$(adb -s "$target" get-state 2>/dev/null)" = device ]; then | |
| $dry_run || echo "adb-wireless: $model is on Wi-Fi adb at $target" | |
| else | |
| echo "adb-wireless: couldn't connect to $target: is the Mac on the same Wi-Fi as the phone?" >&2 | |
| fi | |
| done | |
| } | |
| if $watch; then | |
| while true; do | |
| ensure_once | |
| sleep 30 | |
| done | |
| else | |
| ensure_once | |
| fi |
参与讨论