mirror of
git://f0xx.org/android_cast
synced 2026-07-29 08:57:44 +03:00
498 lines
14 KiB
Bash
Executable File
498 lines
14 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# 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).
|
|
|
|
: "${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() {
|
|
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_hint_key_for_ip() {
|
|
local ip=${1}
|
|
ip="$(echo "${ip}" | sed -e 's@\.@_@g' -e 's@:.*@@' | tr -d '\n')"
|
|
printf '%s' "${ip}"
|
|
}
|
|
|
|
adb_ip_for_hint_key() {
|
|
local ip=${1}
|
|
ip="$(echo "${ip}" | sed -e 's/_/./g' -e 's/:.*//' | tr -d '\n')"
|
|
printf '%s' "${ip}"
|
|
}
|
|
|
|
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
|
|
raw = sys.stdin.read()
|
|
if not raw.strip():
|
|
raise SystemExit(0)
|
|
try:
|
|
data = json.loads(raw)
|
|
except json.JSONDecodeError:
|
|
raise SystemExit(0)
|
|
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))
|
|
for item in data.get("connect") or []:
|
|
if ok(item):
|
|
print(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)
|
|
' 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_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_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
|
|
|
|
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[@]}"
|
|
}
|
|
|
|
# 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
|
|
return 1
|
|
}
|
|
|
|
adb_update_connect_cache() {
|
|
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
|
|
|
|
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
|
|
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 /'
|
|
}
|