mirror of
git://f0xx.org/ac/ac-mobile-android
synced 2026-07-29 01:07:35 +03:00
feat(dev): improve wireless ADB reconnect and mDNS probing
Align in-app dev helpers with adb-reconnect pairing flow and expand tests for Android 11+ TLS wireless debugging. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -26,6 +26,7 @@ public final class DevAdbConnectInfo {
|
||||
public final boolean adbWifiEnabled;
|
||||
public final boolean secureSettingsGranted;
|
||||
public final int legacyPort;
|
||||
public final int pairingPort;
|
||||
public final long updatedMs;
|
||||
public final RaControlEndpoint raControl;
|
||||
|
||||
@@ -50,7 +51,7 @@ public final class DevAdbConnectInfo {
|
||||
boolean secureSettingsGranted,
|
||||
int legacyPort,
|
||||
long updatedMs) {
|
||||
this(ips, connect, adbEnabled, adbWifiEnabled, secureSettingsGranted, legacyPort, updatedMs, null);
|
||||
this(ips, connect, adbEnabled, adbWifiEnabled, secureSettingsGranted, legacyPort, -1, updatedMs, null);
|
||||
}
|
||||
|
||||
public DevAdbConnectInfo(
|
||||
@@ -62,12 +63,26 @@ public final class DevAdbConnectInfo {
|
||||
int legacyPort,
|
||||
long updatedMs,
|
||||
RaControlEndpoint raControl) {
|
||||
this(ips, connect, adbEnabled, adbWifiEnabled, secureSettingsGranted, legacyPort, -1, updatedMs, raControl);
|
||||
}
|
||||
|
||||
public DevAdbConnectInfo(
|
||||
List<String> ips,
|
||||
List<String> connect,
|
||||
boolean adbEnabled,
|
||||
boolean adbWifiEnabled,
|
||||
boolean secureSettingsGranted,
|
||||
int legacyPort,
|
||||
int pairingPort,
|
||||
long updatedMs,
|
||||
RaControlEndpoint raControl) {
|
||||
this.ips = ips != null ? ips : Collections.emptyList();
|
||||
this.connect = connect != null ? connect : Collections.emptyList();
|
||||
this.adbEnabled = adbEnabled;
|
||||
this.adbWifiEnabled = adbWifiEnabled;
|
||||
this.secureSettingsGranted = secureSettingsGranted;
|
||||
this.legacyPort = legacyPort;
|
||||
this.pairingPort = pairingPort;
|
||||
this.updatedMs = updatedMs;
|
||||
this.raControl = raControl;
|
||||
}
|
||||
@@ -90,6 +105,16 @@ public final class DevAdbConnectInfo {
|
||||
root.put("connect", toJsonArray(connect));
|
||||
root.put("tls_port",
|
||||
findFirstNonLegacyPort(connect));
|
||||
if (pairingPort > 0) {
|
||||
root.put("pairing_port", pairingPort);
|
||||
JSONArray pairCmds = new JSONArray();
|
||||
for (String ip : ips) {
|
||||
if (!TextUtils.isEmpty(ip)) {
|
||||
pairCmds.put("adb pair " + ip + ":" + pairingPort);
|
||||
}
|
||||
}
|
||||
root.put("pair_commands", pairCmds);
|
||||
}
|
||||
root.put("needs_secure_grant", !secureSettingsGranted);
|
||||
JSONArray commands = new JSONArray();
|
||||
for (String endpoint : connect) {
|
||||
@@ -98,8 +123,11 @@ public final class DevAdbConnectInfo {
|
||||
}
|
||||
}
|
||||
root.put("commands", commands);
|
||||
root.put("hint", "Grant WRITE_SECURE_SETTINGS once via USB; use tls_port "
|
||||
+ "(not 5555) on Android 11+. Host: avahi-browse -rpt _adb-tls-connect._tcp");
|
||||
root.put("hint", "Android 11+ wireless adb: pair this PC once (adb pair IP:pairing_port CODE), "
|
||||
+ "then adb connect IP:tls_port. Open Wireless debugging → Pair device for the 6-digit code. "
|
||||
+ "Host: avahi-browse -rpt _adb-tls-connect._tcp");
|
||||
root.put("connect_note", "If adb connect fails with 'failed to connect', the PC is not paired — "
|
||||
+ "use pair_commands while the pairing dialog is open on the device.");
|
||||
if (raControl != null && raControl.port > 0) {
|
||||
JSONObject ra = new JSONObject();
|
||||
ra.put("port", raControl.port);
|
||||
|
||||
@@ -24,6 +24,88 @@ final class DevAdbMdnsProbe {
|
||||
|
||||
private DevAdbMdnsProbe() {}
|
||||
|
||||
static List<Integer> discoverPairingPorts(Context context, long timeoutMs) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
NsdManager nsd = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
|
||||
if (nsd == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
Set<Integer> ports = new LinkedHashSet<>();
|
||||
Set<String> localIps = new LinkedHashSet<>(DevAdbWifiHelper.collectWifiIpv4Addresses(context));
|
||||
CountDownLatch done = new CountDownLatch(1);
|
||||
AtomicBoolean stopped = new AtomicBoolean(false);
|
||||
|
||||
NsdManager.DiscoveryListener listener = new NsdManager.DiscoveryListener() {
|
||||
@Override
|
||||
public void onDiscoveryStarted(String serviceType) {}
|
||||
|
||||
@Override
|
||||
public void onServiceFound(NsdServiceInfo serviceInfo) {
|
||||
nsd.resolveService(serviceInfo, new NsdManager.ResolveListener() {
|
||||
@Override
|
||||
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {}
|
||||
|
||||
@Override
|
||||
public void onServiceResolved(NsdServiceInfo info) {
|
||||
int port = info.getPort();
|
||||
if (port <= 0 || port >= 65536) {
|
||||
return;
|
||||
}
|
||||
String host = info.getHost() != null ? info.getHost().getHostAddress() : "";
|
||||
if (!localIps.isEmpty()
|
||||
&& host != null
|
||||
&& !host.isEmpty()
|
||||
&& !localIps.contains(host)) {
|
||||
return;
|
||||
}
|
||||
ports.add(port);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceLost(NsdServiceInfo serviceInfo) {}
|
||||
|
||||
@Override
|
||||
public void onDiscoveryStopped(String serviceType) {
|
||||
if (stopped.compareAndSet(false, true)) {
|
||||
done.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
|
||||
if (stopped.compareAndSet(false, true)) {
|
||||
done.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
|
||||
if (stopped.compareAndSet(false, true)) {
|
||||
done.countDown();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
nsd.discoverServices("_adb-tls-pairing._tcp", NsdManager.PROTOCOL_DNS_SD, listener);
|
||||
if (!done.await(timeoutMs, TimeUnit.MILLISECONDS)) {
|
||||
Log.d(DevAdbConnectInfo.LOG_TAG, "mDNS pairing discovery timed out");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
try {
|
||||
nsd.stopServiceDiscovery(listener);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(ports);
|
||||
}
|
||||
|
||||
static List<Integer> discoverPorts(Context context, long timeoutMs) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
||||
return new ArrayList<>();
|
||||
|
||||
@@ -59,6 +59,7 @@ public final class DevAdbWifiHelper {
|
||||
connect.addAll(parseDumpsysEndpoints(runDumpsysAdb(app)));
|
||||
|
||||
List<String> dedupedConnect = orderConnectEndpoints(dedupe(connect), ports);
|
||||
int pairingPort = discoverPairingPort(app, runDumpsysAdb(app));
|
||||
long now = System.currentTimeMillis();
|
||||
DevAdbConnectInfo.RaControlEndpoint raControl = RemoteAccessControlClient.snapshotForAdb(app);
|
||||
return new DevAdbConnectInfo(
|
||||
@@ -68,6 +69,7 @@ public final class DevAdbWifiHelper {
|
||||
adbWifiEnabled,
|
||||
secure,
|
||||
DevAdbConnectInfo.LEGACY_TCP_PORT,
|
||||
pairingPort,
|
||||
now,
|
||||
raControl);
|
||||
}
|
||||
@@ -282,6 +284,32 @@ public final class DevAdbWifiHelper {
|
||||
return new ArrayList<>(endpoints);
|
||||
}
|
||||
|
||||
/** Pairing port from {@code dumpsys adb} (wireless debug — use with {@code adb pair}, not connect). */
|
||||
static int parseDumpsysPairingPort(String dumpsys) {
|
||||
if (TextUtils.isEmpty(dumpsys)) {
|
||||
return -1;
|
||||
}
|
||||
Pattern pairing = Pattern.compile("(?i)pairing\\s+port[^0-9]{0,20}(\\d{2,5})");
|
||||
Matcher m = pairing.matcher(dumpsys);
|
||||
if (m.find()) {
|
||||
return parsePositiveInt(m.group(1));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int discoverPairingPort(Context context, String dumpsys) {
|
||||
int fromDumpsys = parseDumpsysPairingPort(dumpsys);
|
||||
if (fromDumpsys > 0) {
|
||||
return fromDumpsys;
|
||||
}
|
||||
for (Integer port : DevAdbMdnsProbe.discoverPairingPorts(context, 1500L)) {
|
||||
if (port != null && port > 0) {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static List<String> dedupe(List<String> values) {
|
||||
Set<String> out = new LinkedHashSet<>();
|
||||
for (String value : values) {
|
||||
|
||||
@@ -19,6 +19,12 @@ public class DevAdbWifiHelperTest {
|
||||
assertEquals(1, endpoints.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseDumpsysPairingPort_findsPairingPortLine() {
|
||||
String dumpsys = "TLS port: 45139\nPairing port: 37265\n";
|
||||
assertEquals(37265, DevAdbWifiHelper.parseDumpsysPairingPort(dumpsys));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeEndpoints_buildsCartesianProduct() {
|
||||
List<String> connect = DevAdbConnectInfo.mergeEndpoints(
|
||||
|
||||
@@ -1,43 +1,639 @@
|
||||
#!/usr/bin/env bash
|
||||
# ADB reconnection helpers. Source this file.
|
||||
# Provides: adb_reconnect_init_hints, adb_reconnect_all, adb_http_status, adb_prepare_device_for_deploy, adb_update_connect_cache
|
||||
# Wireless adb discovery + reconnect helpers (sourced by rebuild.sh).
|
||||
# Priority: app HTTP :5039/adb.json → avahi → cache → mdns → :5555 hints.
|
||||
# Only IPv4:PORT is used for adb connect (no mDNS service names).
|
||||
|
||||
DEV_ADB_HTTP_PORT="${DEV_ADB_HTTP_PORT:-5039}"
|
||||
ADB_TCP_PROBE="${ADB_TCP_PROBE:-no}"
|
||||
: "${ADB_CONNECT_RETRIES:=3}"
|
||||
: "${ADB_CONNECT_DELAY_SEC:=1}"
|
||||
: "${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_RECONNECT_POKE_DELAY_SEC:=2}"
|
||||
: "${ADB_MDNS_AUTO_CONNECT:=adb-tls-connect,adb}"
|
||||
: "${ADB_KILL_SERVER:=no}"
|
||||
: "${ADB_DISCONNECT_BEFORE_CONNECT:=no}"
|
||||
|
||||
declare -gA CONNECTIONS_LIST_HINTS
|
||||
|
||||
adb_reconnect_init_hints() {
|
||||
: # Placeholder: load hints from local config if present
|
||||
CONNECTIONS_LIST_HINTS["192_168_33_153"]="[A13] Android Projector"
|
||||
CONNECTIONS_LIST_HINTS["192_168_33_39"]="[A16] Doogee Tab G6 Max / P8_EEA"
|
||||
CONNECTIONS_LIST_HINTS["192_168_33_106"]="[A11] BlackView BL6000Pro / BL6000Pro_EEA"
|
||||
CONNECTIONS_LIST_HINTS["192_168_33_173"]="[A11] BlackView BL6000Pro / BL6000Pro"
|
||||
}
|
||||
|
||||
adb_http_status() {
|
||||
local ip="$1"
|
||||
local port="${2:-$DEV_ADB_HTTP_PORT}"
|
||||
local url="http://${ip}:${port}/adb.json"
|
||||
local result
|
||||
result="$(curl -sf --max-time 3 "$url" 2>/dev/null || echo "{}")"
|
||||
echo "adb_http_status $ip: $result"
|
||||
adb_hint_key_for_ip() {
|
||||
local ip=${1}
|
||||
ip="$(echo "${ip}" | sed -e 's@\.@_@g' -e 's@:.*@@' | tr -d '\n')"
|
||||
printf '%s' "${ip}"
|
||||
}
|
||||
|
||||
adb_reconnect_all() {
|
||||
local label="${1:-reconnect}"
|
||||
adb_ip_for_hint_key() {
|
||||
local ip=${1}
|
||||
ip="$(echo "${ip}" | sed -e 's/_/./g' -e 's/:.*//' | tr -d '\n')"
|
||||
printf '%s' "${ip}"
|
||||
}
|
||||
|
||||
# Try any IPs registered via HTTP discovery
|
||||
if [[ "$ADB_TCP_PROBE" == "yes" ]]; then
|
||||
for ip in $(adb devices | awk 'NR>1 {print $1}' | grep -E '^[0-9]+\.' | cut -d: -f1 | sort -u); do
|
||||
adb connect "$ip:5555" >/dev/null 2>&1 || true
|
||||
done
|
||||
adb_hint_for_endpoint() {
|
||||
local endpoint=${1}
|
||||
local key
|
||||
key="$(adb_hint_key_for_ip "${endpoint}")"
|
||||
printf '%s' "${CONNECTIONS_LIST_HINTS[${key}]:-}"
|
||||
}
|
||||
|
||||
adb_endpoint_host() {
|
||||
local endpoint=${1}
|
||||
if [[ "$endpoint" =~ ^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+): ]]; then
|
||||
printf '%s' "${BASH_REMATCH[1]}"
|
||||
fi
|
||||
}
|
||||
|
||||
adb_is_connectable_ip_port() {
|
||||
local endpoint=${1}
|
||||
[[ "$endpoint" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+$ ]]
|
||||
}
|
||||
|
||||
adb_tcp_port_open() {
|
||||
local endpoint=${1}
|
||||
local host port
|
||||
host="$(adb_endpoint_host "$endpoint")"
|
||||
port="${endpoint##*:}"
|
||||
[[ -n "$host" && "$port" =~ ^[0-9]+$ ]] || return 1
|
||||
if command -v nc >/dev/null 2>&1; then
|
||||
nc -z -w "${ADB_TCP_PROBE_SEC}" "$host" "$port" >/dev/null 2>&1
|
||||
return $?
|
||||
fi
|
||||
timeout "${ADB_TCP_PROBE_SEC}" bash -c "exec 3<>/dev/tcp/${host}/${port}" 2>/dev/null
|
||||
}
|
||||
|
||||
adb_collect_candidate_ips() {
|
||||
local ip endpoint key
|
||||
if [[ -f "$ADB_CONNECT_CACHE" ]]; then
|
||||
while IFS= read -r endpoint; do
|
||||
[[ -z "$endpoint" ]] && continue
|
||||
ip="${endpoint%%:*}"
|
||||
[[ -n "$ip" ]] && printf '%s\n' "$ip"
|
||||
done < "$ADB_CONNECT_CACHE"
|
||||
fi
|
||||
for key in "${!CONNECTIONS_LIST_HINTS[@]}"; do
|
||||
adb_ip_for_hint_key "$key"
|
||||
done
|
||||
adb devices -l 2>/dev/null | awk '/^[^ ]+ +device/ { print $1 }' | while read -r serial; do
|
||||
[[ "$serial" == *:* ]] || continue
|
||||
printf '%s\n' "${serial%%:*}"
|
||||
done
|
||||
}
|
||||
|
||||
adb_json_valid() {
|
||||
local json=${1}
|
||||
[[ -n "$json" ]] || return 1
|
||||
printf '%s' "$json" | python3 -c 'import json,sys; json.loads(sys.stdin.read())' 2>/dev/null
|
||||
}
|
||||
|
||||
adb_json_connect_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)
|
||||
tls = int(data.get("tls_port") or 0)
|
||||
if tls > 0:
|
||||
for ip in data.get("ips") or []:
|
||||
if ip:
|
||||
emit(f"{ip}:{tls}")
|
||||
for item in data.get("connect") or []:
|
||||
emit(item)
|
||||
for item in data.get("commands") or []:
|
||||
if isinstance(item, str) and item.startswith("adb connect "):
|
||||
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
|
||||
}
|
||||
|
||||
adb_curl_adb_json() {
|
||||
local ip=${1}
|
||||
local json cached="${ADB_HTTP_CACHE_DIR}/${ip}.json"
|
||||
json="$(curl -m 5 -sS "http://${ip}:${DEV_ADB_HTTP_PORT}/adb.json" 2>/dev/null || true)"
|
||||
if adb_json_valid "$json"; then
|
||||
mkdir -p "$ADB_HTTP_CACHE_DIR"
|
||||
printf '%s' "$json" >"$cached"
|
||||
printf '%s' "$json"
|
||||
return 0
|
||||
fi
|
||||
if [[ -f "$cached" ]] && adb_json_valid "$(cat "$cached")"; then
|
||||
cat "$cached"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
adb_fetch_http_endpoints() {
|
||||
local ip json
|
||||
for ip in $(adb_collect_candidate_ips | sort -u); do
|
||||
[[ -z "$ip" ]] && continue
|
||||
json="$(adb_curl_adb_json "$ip" 2>/dev/null || true)"
|
||||
[[ -z "$json" ]] && continue
|
||||
adb_json_connect_lines "$json"
|
||||
done
|
||||
}
|
||||
|
||||
adb_discover_mdns_endpoints() {
|
||||
local line hostport
|
||||
while IFS= read -r line; do
|
||||
[[ "$line" == List* ]] && continue
|
||||
[[ "$line" == error:* ]] && continue
|
||||
[[ "$line" == *"_adb"* ]] || continue
|
||||
hostport="$(awk '{print $NF}' <<<"$line")"
|
||||
if adb_is_connectable_ip_port "$hostport"; then
|
||||
printf '%s\n' "$hostport"
|
||||
fi
|
||||
done < <(adb mdns services 2>&1 || true)
|
||||
}
|
||||
|
||||
adb_discover_avahi_endpoints() {
|
||||
command -v avahi-browse >/dev/null 2>&1 || return 0
|
||||
local svc
|
||||
for svc in _adb-tls-connect._tcp _adb._tcp; do
|
||||
avahi-browse -rpt "$svc" 2>/dev/null | awk -F';' '
|
||||
$1 == "=" && $3 == "IPv4" && $8 ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ && $9 ~ /^[0-9]+$/ {
|
||||
print $8 ":" $9
|
||||
}'
|
||||
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
|
||||
[[ -z "$serial" ]] && continue
|
||||
tmp="$(mktemp)"
|
||||
if adb -s "$serial" exec-out run-as com.foxx.androidcast cat files/dev/adb_connect.json \
|
||||
>"$tmp" 2>/dev/null; then
|
||||
json="$(cat "$tmp")"
|
||||
rm -f "$tmp"
|
||||
adb_json_connect_lines "$json"
|
||||
else
|
||||
rm -f "$tmp"
|
||||
fi
|
||||
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
|
||||
printf '9'
|
||||
else
|
||||
printf '2'
|
||||
fi
|
||||
}
|
||||
|
||||
# One endpoint per IPv4 host; keep best rank (HTTP/avahi TLS beats :5555).
|
||||
adb_discover_endpoints() {
|
||||
local endpoint host rank best_rank existing
|
||||
declare -A seen=()
|
||||
declare -A host_best=()
|
||||
declare -A host_rank=()
|
||||
local -a ordered=()
|
||||
|
||||
consider_endpoint() {
|
||||
local e=${1}
|
||||
[[ -z "$e" ]] && return 0
|
||||
if [[ "$e" == *_* && "$e" != *":"* ]]; then
|
||||
e="$(adb_ip_for_hint_key "$e")"
|
||||
fi
|
||||
[[ -z "$e" ]] && return 0
|
||||
if ! adb_is_connectable_ip_port "$e"; then
|
||||
return 0
|
||||
fi
|
||||
host="$(adb_endpoint_host "$e")"
|
||||
rank="$(adb_endpoint_rank "$e")"
|
||||
if [[ -n "$host" ]]; then
|
||||
existing="${host_best[$host]:-}"
|
||||
if [[ -n "$existing" ]]; then
|
||||
best_rank="${host_rank[$host]}"
|
||||
if [[ "$rank" -ge "$best_rank" ]]; then
|
||||
return 0
|
||||
fi
|
||||
seen["$existing"]=0
|
||||
for i in "${!ordered[@]}"; do
|
||||
if [[ "${ordered[$i]}" == "$existing" ]]; then
|
||||
unset 'ordered[i]'
|
||||
fi
|
||||
done
|
||||
ordered=("${ordered[@]}")
|
||||
fi
|
||||
host_best["$host"]="$e"
|
||||
host_rank["$host"]="$rank"
|
||||
fi
|
||||
[[ -n "${seen[$e]:-}" ]] && return 0
|
||||
seen["$e"]=1
|
||||
ordered+=("$e")
|
||||
}
|
||||
|
||||
# HTTP first — matches curl the user runs manually.
|
||||
while IFS= read -r endpoint; do
|
||||
consider_endpoint "$endpoint"
|
||||
done < <(adb_fetch_http_endpoints)
|
||||
|
||||
while IFS= read -r endpoint; do
|
||||
consider_endpoint "$endpoint"
|
||||
done < <(adb_discover_avahi_endpoints)
|
||||
|
||||
if [[ -f "$ADB_CONNECT_CACHE" ]]; then
|
||||
while IFS= read -r endpoint; do
|
||||
consider_endpoint "$endpoint"
|
||||
done < "$ADB_CONNECT_CACHE"
|
||||
fi
|
||||
|
||||
# mDNS / default discovery (adb already handles this)
|
||||
true
|
||||
while IFS= read -r endpoint; do
|
||||
consider_endpoint "$endpoint"
|
||||
done < <(adb_pull_device_json_endpoints)
|
||||
|
||||
while IFS= read -r endpoint; do
|
||||
consider_endpoint "$endpoint"
|
||||
done < <(adb_discover_mdns_endpoints)
|
||||
|
||||
local key ip
|
||||
for key in "${!CONNECTIONS_LIST_HINTS[@]}"; do
|
||||
ip="$(adb_ip_for_hint_key "$key")"
|
||||
[[ -n "${host_best[$ip]:-}" ]] && continue
|
||||
consider_endpoint "${ip}:5555"
|
||||
done
|
||||
|
||||
adb_filter_endpoints_by_tcp "${ordered[@]}"
|
||||
}
|
||||
|
||||
adb_prepare_device_for_deploy() {
|
||||
local serial="$1"
|
||||
# Disable screen lock prompts, maximize brightness — non-critical
|
||||
adb -s "$serial" shell settings put global package_verifier_enable 0 >/dev/null 2>&1 || true
|
||||
# Drop closed ports (fast nc/bash probe). If none open, return candidates anyway.
|
||||
adb_filter_endpoints_by_tcp() {
|
||||
local -a all=("$@")
|
||||
local -a open=()
|
||||
local e
|
||||
if [[ "${ADB_TCP_PROBE}" =~ ^[nN] ]]; then
|
||||
printf '%s\n' "${all[@]}"
|
||||
return 0
|
||||
fi
|
||||
for e in "${all[@]}"; do
|
||||
[[ -z "$e" ]] && continue
|
||||
if adb_tcp_port_open "$e"; then
|
||||
open+=("$e")
|
||||
else
|
||||
echo " probe ${e} — closed (skip)" >&2
|
||||
fi
|
||||
done
|
||||
if [[ ${#open[@]} -gt 0 ]]; then
|
||||
printf '%s\n' "${open[@]}"
|
||||
else
|
||||
printf '%s\n' "${all[@]}"
|
||||
fi
|
||||
}
|
||||
|
||||
adb_poke_refresh_broadcast() {
|
||||
local serial
|
||||
while IFS= read -r serial; do
|
||||
[[ -z "$serial" ]] && continue
|
||||
adb -s "$serial" shell am broadcast \
|
||||
-a com.foxx.androidcast.dev.REFRESH_ADB_WIFI \
|
||||
-n com.foxx.androidcast/.dev.DevAdbWifiReceiver >/dev/null 2>&1 || true
|
||||
done < <(adb devices 2>/dev/null | awk 'NR > 1 && $2 == "device" { print $1 }')
|
||||
}
|
||||
|
||||
# Wireless TLS adb often shows as adb-xxx._adb-tls-connect._tcp, not IP:port.
|
||||
adb_resolve_device_serial() {
|
||||
local endpoint=${1}
|
||||
local serial state host count=0 last=""
|
||||
if adb -s "$endpoint" get-state 2>/dev/null | grep -q '^device$'; then
|
||||
printf '%s' "$endpoint"
|
||||
return 0
|
||||
fi
|
||||
host="$(adb_endpoint_host "$endpoint")"
|
||||
while IFS= read -r serial state; do
|
||||
[[ "$state" != "device" ]] && continue
|
||||
count=$((count + 1))
|
||||
last="$serial"
|
||||
if [[ "$serial" == "$endpoint" ]]; then
|
||||
printf '%s' "$serial"
|
||||
return 0
|
||||
fi
|
||||
if [[ -n "$host" && "$serial" == *"$host"* ]]; then
|
||||
printf '%s' "$serial"
|
||||
return 0
|
||||
fi
|
||||
done < <(adb devices 2>/dev/null | awk 'NR > 1 && NF >= 2 { print $1, $2 }')
|
||||
if [[ "$count" -eq 1 && -n "$last" ]]; then
|
||||
printf '%s' "$last"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
adb_parse_connect_serial() {
|
||||
local out=${1}
|
||||
if [[ "$out" =~ [Cc]onnected\ to\ ([^[:space:]]+) ]]; then
|
||||
printf '%s' "${BASH_REMATCH[1]}"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
adb_host_has_device() {
|
||||
local host=${1}
|
||||
local serial
|
||||
serial="$(adb_resolve_device_serial "${host}:1" 2>/dev/null || true)"
|
||||
[[ -n "$serial" ]]
|
||||
}
|
||||
|
||||
adb_reconnect_endpoint() {
|
||||
local endpoint="$1"
|
||||
[[ -z "$endpoint" ]] && return 1
|
||||
if ! adb_is_connectable_ip_port "$endpoint"; then
|
||||
echo " skip ${endpoint} — not IPv4:PORT" >&2
|
||||
return 1
|
||||
fi
|
||||
local attempt out hint resolved
|
||||
hint="$(adb_hint_for_endpoint "${endpoint}")"
|
||||
[[ -n "${hint}" ]] && hint="(${hint})"
|
||||
if [[ "${ADB_TCP_PROBE}" =~ [yY] ]] && ! adb_tcp_port_open "$endpoint"; then
|
||||
echo " skip ${endpoint} — TCP closed ${hint}" >&2
|
||||
return 1
|
||||
fi
|
||||
resolved="$(adb_resolve_device_serial "$endpoint" 2>/dev/null || true)"
|
||||
if [[ -n "$resolved" ]]; then
|
||||
echo " ${endpoint} already connected as ${resolved} ${hint}" >&2
|
||||
printf '%s' "$resolved"
|
||||
return 0
|
||||
fi
|
||||
echo " adb connect ${endpoint} ${hint}" >&2
|
||||
if [[ "${ADB_DISCONNECT_BEFORE_CONNECT}" =~ [yY] ]]; then
|
||||
adb disconnect "$endpoint" >/dev/null 2>&1 || true
|
||||
fi
|
||||
for (( attempt = 1; attempt <= ADB_CONNECT_RETRIES; attempt++ )); do
|
||||
out="$(adb connect "$endpoint" 2>&1)" || true
|
||||
sleep 1
|
||||
resolved="$(adb_parse_connect_serial "$out" 2>/dev/null || true)"
|
||||
if [[ -z "$resolved" ]]; then
|
||||
resolved="$(adb_resolve_device_serial "$endpoint" 2>/dev/null || true)"
|
||||
fi
|
||||
if [[ -n "$resolved" ]]; then
|
||||
echo " ${resolved} — ready (${out})" >&2
|
||||
printf '%s' "$resolved"
|
||||
return 0
|
||||
fi
|
||||
if [[ "$attempt" -lt "$ADB_CONNECT_RETRIES" ]]; then
|
||||
sleep "$ADB_CONNECT_DELAY_SEC"
|
||||
fi
|
||||
done
|
||||
echo " ${endpoint} — not reachable (${out})" >&2
|
||||
adb_connect_failure_hint "$endpoint" "$out"
|
||||
return 1
|
||||
}
|
||||
|
||||
adb_update_connect_cache() {
|
||||
: # Placeholder: persist known device IPs
|
||||
local serial
|
||||
mkdir -p "$(dirname "$ADB_CONNECT_CACHE")"
|
||||
: >"$ADB_CONNECT_CACHE"
|
||||
while IFS= read -r serial; do
|
||||
adb_is_connectable_ip_port "$serial" || continue
|
||||
echo "$serial" >>"$ADB_CONNECT_CACHE"
|
||||
done < <(adb devices 2>/dev/null | awk 'NR > 1 && $2 == "device" { print $1 }')
|
||||
}
|
||||
|
||||
adb_reconnect_prepare() {
|
||||
export ADB_MDNS_AUTO_CONNECT
|
||||
adb start-server >/dev/null 2>&1 || true
|
||||
if [[ "${ADB_KILL_SERVER}" =~ [yY] ]]; then
|
||||
adb kill-server >/dev/null 2>&1 || true
|
||||
adb start-server >/dev/null 2>&1 || true
|
||||
fi
|
||||
adb reconnect offline >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
adb_reconnect_all() {
|
||||
local label=${1:-post-build}
|
||||
echo "==> Reconnecting wireless adb targets (${label})"
|
||||
adb_reconnect_prepare
|
||||
adb_poke_refresh_broadcast
|
||||
if [[ "${ADB_RECONNECT_POKE_DELAY_SEC:-0}" -gt 0 ]]; then
|
||||
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
|
||||
host="$(adb_endpoint_host "$serial")"
|
||||
[[ -n "$host" ]] && connected_hosts["$host"]="$serial"
|
||||
done < <(adb devices 2>/dev/null | awk 'NR > 1 && $2 == "device" { print $1 }')
|
||||
|
||||
while IFS= read -r endpoint; do
|
||||
[[ -z "$endpoint" ]] && continue
|
||||
host="$(adb_endpoint_host "$endpoint")"
|
||||
if [[ -n "$host" && -n "${connected_hosts[$host]:-}" ]]; then
|
||||
echo " skip ${endpoint} — ${host} already on ${connected_hosts[$host]}"
|
||||
continue
|
||||
fi
|
||||
if resolved="$(adb_reconnect_endpoint "$endpoint")"; then
|
||||
connected=$((connected + 1))
|
||||
host="$(adb_endpoint_host "$resolved")"
|
||||
[[ -z "$host" ]] && host="$(adb_endpoint_host "$endpoint")"
|
||||
[[ -n "$host" ]] && connected_hosts["$host"]="$resolved"
|
||||
fi
|
||||
done < <(adb_discover_endpoints)
|
||||
|
||||
if [[ "$connected" -eq 0 ]]; then
|
||||
echo " retry: HTTP-only pass"
|
||||
while IFS= read -r endpoint; do
|
||||
[[ -z "$endpoint" ]] && continue
|
||||
if resolved="$(adb_reconnect_endpoint "$endpoint")"; then
|
||||
connected=$((connected + 1))
|
||||
fi
|
||||
done < <(adb_fetch_http_endpoints)
|
||||
fi
|
||||
|
||||
adb_update_connect_cache
|
||||
adb devices -l
|
||||
if [[ "$connected" -gt 0 ]]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
adb_count_ready_devices() {
|
||||
adb devices 2>/dev/null | awk 'NR > 1 && $2 == "device" { c++ } END { print c+0 }'
|
||||
}
|
||||
|
||||
adb_prepare_device_for_deploy() {
|
||||
local serial=${1:-}
|
||||
local -a adb_args=()
|
||||
[[ -n "$serial" ]] && adb_args=(-s "$serial")
|
||||
adb "${adb_args[@]}" shell input keyevent KEYCODE_WAKEUP >/dev/null 2>&1 \
|
||||
|| adb "${adb_args[@]}" shell input keyevent 224 >/dev/null 2>&1 \
|
||||
|| true
|
||||
adb "${adb_args[@]}" shell am broadcast \
|
||||
-a com.foxx.androidcast.dev.PREPARE_FOR_DEPLOY \
|
||||
-n com.foxx.androidcast/.dev.DebugDeployWakeReceiver >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
adb_http_status() {
|
||||
local ip=${1}
|
||||
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)"
|
||||
return 1
|
||||
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 <<EOF
|
||||
Usage: $(basename "$0") <command> [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
|
||||
|
||||
Reference in New Issue
Block a user