From 0026d9658596af791cc1457d3ea6b00a63e533c8 Mon Sep 17 00:00:00 2001 From: Anton Afanasyeu Date: Fri, 10 Jul 2026 15:36:49 +0200 Subject: [PATCH] feat(adb): improve wireless reconnect with pairing hints Expand adb-reconnect.sh with TLS pairing guidance, dedupe, and safer cache handling for Android 11+ wireless debugging. Co-authored-by: Cursor --- adb-reconnect.sh | 170 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 156 insertions(+), 14 deletions(-) diff --git a/adb-reconnect.sh b/adb-reconnect.sh index 3cbc195..2dd2266 100755 --- a/adb-reconnect.sh +++ b/adb-reconnect.sh @@ -8,8 +8,8 @@ : "${ADB_TCP_PROBE:=yes}" : "${ADB_TCP_PROBE_SEC:=1}" : "${DEV_ADB_HTTP_PORT:=5039}" -: "${ADB_CONNECT_CACHE:=$ROOT/.adb_connect_cache}" -: "${ADB_HTTP_CACHE_DIR:=$ROOT/.adb_http_cache}" +: "${ADB_CONNECT_CACHE:=${ROOT:-.}/.adb_connect_cache}" +: "${ADB_HTTP_CACHE_DIR:=${ROOT:-.}/.adb_http_cache}" : "${ADB_RECONNECT_POKE_DELAY_SEC:=2}" : "${ADB_MDNS_AUTO_CONNECT:=adb-tls-connect,adb}" : "${ADB_KILL_SERVER:=no}" @@ -96,7 +96,7 @@ adb_json_connect_lines() { local json=${1} [[ -n "$json" ]] || return 0 printf '%s' "$json" | python3 -c ' -import json, sys +import json, sys, re raw = sys.stdin.read() if not raw.strip(): raise SystemExit(0) @@ -104,23 +104,51 @@ try: data = json.loads(raw) except json.JSONDecodeError: raise SystemExit(0) +IP_PORT = re.compile(r"^\d{1,3}(?:\.\d{1,3}){3}:\d+$") +seen = set() +def emit(ep): + if ep and IP_PORT.match(ep) and ep not in seen: + seen.add(ep) + print(ep) tls = int(data.get("tls_port") or 0) if tls > 0: for ip in data.get("ips") or []: if ip: - print(f"{ip}:{tls}") -import re -IP_PORT = re.compile(r"^\d{1,3}(?:\.\d{1,3}){3}:\d+$") -def ok(ep): - return bool(ep and IP_PORT.match(ep)) + emit(f"{ip}:{tls}") for item in data.get("connect") or []: - if ok(item): - print(item) + emit(item) for item in data.get("commands") or []: if isinstance(item, str) and item.startswith("adb connect "): - ep = item[len("adb connect "):] - if ok(ep): - print(ep) + emit(item[len("adb connect "):]) +' 2>/dev/null || true +} + +adb_json_pairing_lines() { + local json=${1} + [[ -n "$json" ]] || return 0 + printf '%s' "$json" | python3 -c ' +import json, sys, re +raw = sys.stdin.read() +if not raw.strip(): + raise SystemExit(0) +try: + data = json.loads(raw) +except json.JSONDecodeError: + raise SystemExit(0) +IP_PORT = re.compile(r"^\d{1,3}(?:\.\d{1,3}){3}:\d+$") +seen = set() +def emit(ep): + if ep and IP_PORT.match(ep) and ep not in seen: + seen.add(ep) + print(ep) +pair = int(data.get("pairing_port") or 0) +if pair > 0: + for ip in data.get("ips") or []: + if ip: + emit(f"{ip}:{pair}") +for item in data.get("pair_commands") or []: + if isinstance(item, str) and item.startswith("adb pair "): + emit(item[len("adb pair "):]) ' 2>/dev/null || true } @@ -175,6 +203,14 @@ adb_discover_avahi_endpoints() { done } +adb_discover_avahi_pairing_endpoints() { + command -v avahi-browse >/dev/null 2>&1 || return 0 + avahi-browse -rpt _adb-tls-pairing._tcp 2>/dev/null | awk -F';' ' + $1 == "=" && $3 == "IPv4" && $8 ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ && $9 ~ /^[0-9]+$/ { + print $8 ":" $9 + }' +} + adb_pull_device_json_endpoints() { local serial json tmp while IFS= read -r serial; do @@ -191,6 +227,60 @@ adb_pull_device_json_endpoints() { done < <(adb devices 2>/dev/null | awk 'NR > 1 && $2 == "device" { print $1 }') } +adb_connect_failure_hint() { + local endpoint=${1} + local out=${2} + local host ip json pair_ep + host="$(adb_endpoint_host "$endpoint")" + [[ -n "$host" ]] || return 0 + if [[ "$out" != *"failed to connect"* && "$out" != *"failed to authenticate"* ]]; then + return 0 + fi + echo " hint: Android 11+ wireless adb requires one-time TLS pairing on this PC." >&2 + json="$(adb_curl_adb_json "$host" 2>/dev/null || true)" + if [[ -n "$json" ]]; then + while IFS= read -r pair_ep; do + [[ -z "$pair_ep" ]] && continue + echo " 1) On device: Developer options → Wireless debugging → Pair device with pairing code" >&2 + echo " 2) adb pair ${pair_ep} (enter 6-digit code when prompted)" >&2 + echo " 3) adb connect ${endpoint}" >&2 + return 0 + done < <(adb_json_pairing_lines "$json") + fi + while IFS= read -r pair_ep; do + [[ -z "$pair_ep" ]] && continue + echo " pairing (avahi): adb pair ${pair_ep}" >&2 + return 0 + done < <(adb_discover_avahi_pairing_endpoints | grep "^${host}:" || true) + echo " Open Wireless debugging → Pair device; then: ADB_PAIR_CODE=123456 $0 adb_pair ${host}" >&2 +} + +adb_pair_device() { + local ip=${1} + local code=${2:-${ADB_PAIR_CODE:-}} + local pair_ep json + [[ -n "$ip" ]] || { echo "usage: adb_pair IP [6-digit-code]" >&2; return 1; } + json="$(adb_curl_adb_json "$ip" 2>/dev/null || true)" + pair_ep="$(adb_json_pairing_lines "$json" 2>/dev/null | grep "^${ip}:" | head -1 || true)" + if [[ -z "$pair_ep" ]]; then + pair_ep="$(adb_discover_avahi_pairing_endpoints | grep "^${ip}:" | head -1 || true)" + fi + if [[ -z "$pair_ep" && -n "${ADB_PAIRING_PORT:-}" ]]; then + pair_ep="${ip}:${ADB_PAIRING_PORT}" + fi + if [[ -z "$pair_ep" ]]; then + echo "${ip}: no pairing port (open Wireless debugging → Pair device with pairing code on tablet)" >&2 + return 1 + fi + if [[ -z "$code" ]]; then + echo "${ip}: pair endpoint ${pair_ep}" >&2 + echo "Run: ADB_PAIR_CODE=123456 $0 adb_pair ${ip}" >&2 + return 1 + fi + echo "==> adb pair ${pair_ep}" >&2 + adb pair "${pair_ep}" "$code" +} + adb_endpoint_rank() { local e=${1} if [[ "$e" == *":5555" ]]; then @@ -396,6 +486,7 @@ adb_reconnect_endpoint() { fi done echo " ${endpoint} — not reachable (${out})" >&2 + adb_connect_failure_hint "$endpoint" "$out" return 1 } @@ -428,6 +519,13 @@ adb_reconnect_all() { sleep "$ADB_RECONNECT_POKE_DELAY_SEC" fi + if [[ -n "${ADB_PAIR_CODE:-}" ]]; then + local _pair_ip + for _pair_ip in $(adb_collect_candidate_ips | sort -u); do + adb_pair_device "$_pair_ip" "$ADB_PAIR_CODE" && echo " paired ${_pair_ip}" >&2 || true + done + fi + local endpoint connected=0 host resolved declare -A connected_hosts=() while IFS= read -r serial; do @@ -486,7 +584,7 @@ adb_prepare_device_for_deploy() { adb_http_status() { local ip=${1} - local json + local json note json="$(adb_curl_adb_json "$ip" 2>/dev/null || true)" if [[ -z "$json" ]]; then echo "${ip}: no response (app not running or port ${DEV_ADB_HTTP_PORT} closed)" @@ -494,4 +592,48 @@ adb_http_status() { fi echo "${ip}: ok" adb_json_connect_lines "$json" | sed 's/^/ connect /' + if adb_json_pairing_lines "$json" | grep -q .; then + adb_json_pairing_lines "$json" | sed 's/^/ pair /' + fi + note="$(printf '%s' "$json" | python3 -c 'import json,sys +try: + d=json.load(sys.stdin) + print(d.get("connect_note") or d.get("hint") or "", end="") +except Exception: + pass' 2>/dev/null || true)" + if [[ -n "$note" ]]; then + echo " note: ${note}" + fi } + +# --- CLI (when executed, not sourced) --- +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + _ADB_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + ROOT="${ROOT:-$(cd "${_ADB_SCRIPT_DIR}/../ac-mobile-android" 2>/dev/null && pwd || pwd)}" + export ROOT + adb_reconnect_init_hints + cmd=${1:-help} + shift || true + case "$cmd" in + adb_http_status) adb_http_status "$@"; exit $? ;; + adb_reconnect_all) adb_reconnect_all "$@"; exit $? ;; + adb_pair) adb_pair_device "$@"; exit $? ;; + help|-h|--help) + cat < [args] + +Commands: + adb_http_status IP curl :5039/adb.json and show connect/pair endpoints + adb_reconnect_all [label] discover + adb connect (HTTP → avahi → cache) + adb_pair IP [CODE] adb pair (CODE or env ADB_PAIR_CODE; port from JSON/avahi) + +Env: DEV_ADB_HTTP_PORT=5039, ADB_PAIR_CODE=123456, ADB_PAIRING_PORT=NNNN, ROOT=project dir +EOF + exit 0 + ;; + *) + echo "Unknown command: $cmd (try help)" >&2 + exit 1 + ;; + esac +fi