1
0
forked from BRT/arc
arc/docs/wifi-adb.md

14 KiB
Raw Permalink Blame History

ADB через Wi-Fi (TCP/IP)

ARC умеет работать с устройствами не только по USB-кабелю, но и по Wi-Fi (ADB-over-TCP). Включается автоматически, без участия пользователя: USB остаётся основным транспортом, Wi-Fi — горячий резерв, который подхватывается при отключении кабеля.

Поведение

  1. Пользователь, как и раньше, подключает телефон по USB.
  2. Фоновый DeviceScreener, обнаружив простаивающее USB-устройство без живого Wi-Fi-эндпоинта, автоматически:
    • читает IP телефона в сети Wi-Fi (wlan0) и его MAC;
    • переводит adbd в TCP-режим (adb tcpip <port>);
    • подключается по сети (adb connect ip:port);
    • сохраняет эндпоинт в settings (ключ wifi_endpoints, JSON { android_id: { "endpoint": "ip:port", "mac": "aa:bb:.." } }; старый формат — значение-строка "ip:port" — читается прозрачно).
  3. После этого кабель можно отключить — устройство остаётся онлайн (статус привязан к android_id, а не к ADB-serial; меняется только adbSerial на ip:port).
  4. При старте ARC и периодически воркер переподключает сохранённые эндпоинты (adb connect), так что устройство, у которого кабель уже отключён, само появляется онлайн.
  5. Если беспроводной эндпоинт «завис», воркер чинит его пересозданием TCP (adb disconnectadb connect): голый повторный connect вернул бы already connected и ничего бы не починил. disconnect ещё и расклинивает это «залипшее» состояние — после него последующие connect (в т.ч. из периодического reconnect) становятся реальными попытками. Срабатывает по двум сигналам:
    • эндпоинт виден в adb devices со статусом offline;
    • устройство ушло в debounced-offline (30 c не отвечает), хотя adb devices всё ещё держит его как device — частый реальный случай «полумёртвого» соединения при слабом Wi-Fi / уходе телефона в doze, когда команды виснут, а статус не меняется (принудительный disconnect→connect, «kick», на переходе в debounced-offline). Починка пропускается для устройств с активной/ожидающей задачей (см. инварианты).
  6. Если connect по сохранённому IP не удался (адрес сменился по DHCP), воркер ищет устройство в сети по сохранённому MAC (ARP-таблица ОС, при включённой опции — с прогревом через ping-скан /24) и переподключается на новый IP, обновляя эндпоинт.

Инварианты

  • USB приоритетнее. Если один android_id виден одновременно по USB-serial и по ip:port, каноническим adbSerial (по нему ходят банк-скрипты) выбирается USB. Wi-Fi-эндпоинт остаётся резервом и используется только когда USB пропадёт.
  • Разрывающие операции — только на простаивающих устройствах. adb tcpip перезапускает adbd, а принудительный disconnect→connect (kick) рвёт текущее соединение — поэтому ни bootstrap, ни kick не выполняются, если на устройстве есть активная/ожидающая задача (тот же сигнал занятости EventDAO::getDevicesWithPendingEvents(), по которому EventHandler не назначает задачи). Иначе разрыв оборвал бы выполняющийся bank-скрипт.
  • Бесшовное отключение кабеля. К моменту отключения USB Wi-Fi-эндпоинт уже поднят и виден в adb devices, поэтому перехода в офлайн не происходит.

Конфигурация (config.ini, секция [wifi])

Ключ Назначение Дефолт
enabled Полностью вкл/выкл функционал true
port TCP-порт adbd для tcpip-режима 5555
reconnectIntervalSecs Как часто переподключать сохранённые эндпоинты 30
bootstrapRetryIntervalSecs Мин. интервал между попытками bootstrap одного устройства 120
macRediscoverEnabled Искать устройство по MAC при смене IP (DHCP) true
macRediscoverIntervalSecs Мин. интервал между попытками поиска по MAC 300
subnetSweepEnabled Прогрев ARP ping-сканом /24 (медленно/шумно) false

Ограничения

  • Перезагрузка телефона сбрасывает tcpip-режим (adbd возвращается в USB-only). Wi-Fi-резерв поднимется заново при следующем подключении кабеля.
  • Смена IP (новый адрес по DHCP) обрабатывается поиском по MAC: при неуспешном adb connect воркер находит устройство в ARP-таблице по сохранённому MAC и переподключается на новый IP. Если устройства нет в ARP, помогает либо включённый subnetSweepEnabled (ping-скан /24 — медленно, шумно), либо повторный bootstrap по USB. Поиск по MAC требует, чтобы ARC и устройство были в одной L2-подсети (ARP не маршрутизируется), и работает только когда MAC был снят при bootstrap.
  • Требуется, чтобы телефон и десктоп были в одной сети Wi-Fi (маршрутизируемой до ip:port устройства).
  • Чтение MAC идёт через ip addr show wlan0 (та же команда, что и для IP), фолбэк — cmd wifi status. Оба проходят без root, включая MIUI/HyperOS (где cat /sys/... и ip link show отдают Permission denied). Обычно это рандомизированный per-SSID MAC, но он стабилен для сети и совпадает с тем, что видит ARP-таблица ПК. Если MAC снять не удалось — эндпоинт хранится без него: теряется только восстановление при смене IP, остальное (bootstrap, reconnect, kick) работает.
  • Скриншоты по Wi-Fi медленнее. Основной путь захвата — http через APK arc-shot (байты идут мимо adb, см. [screenshot] в config.ini); при его недоступности — fallback на exec-out screencap, который отдаёт полноразмерный PNG через adb и по сети тяжелее USB. Для wireless-serial таймаут fallback-захвата поднят (12 c против 10 c по USB). Интервал превью задаётся [screenshot] (previewIntervalHttpSecs для http, previewIntervalSecs для exec-out) и от транспорта Wi-Fi не зависит напрямую. На детект online/offline не влияет; на слабом линке отдельные кадры всё равно могут не успевать.
  • Залипшее соединение vs. статус offline. На практике мёртвый Wi-Fi часто остаётся в adb devices как device (команды виснут), а не переходит в offline — поэтому основной триггер починки именно debounced-offline, а не статус offline (см. Поведение).

Требования к сети

Не требуют доработок ARC — это настройки сети/развёртывания.

Обязательно (иначе Wi-Fi-ADB не работает):

  • ПК с ARC и устройства — в одной L2-подсети (или маршрутизируемой до ip:5555).
  • На роутере отключить AP isolation (изоляцию клиентов Wi-Fi) — иначе порт 5555 между узлами недоступен.
  • Единый adb (бандл tools/adb/...), один adb-сервер — чтобы списки устройств у ARC и фоновых операций не расходились.

Опционально (предсказуемый адрес устройства — снижает число переподключений): смену IP по DHCP ARC и так переживает поиском по MAC (см. Ограничения), поэтому ниже — не требования, а способы сделать адрес стабильным:

Способ Где настраивать Работает на Android <11
DHCP-резервация по MAC роутер да
Статический IP на самом устройстве каждое устройство да
mDNS-имя (adb connect <имя>.local) устройство + сеть нет (только 11+; в ARC не используется)
Поиск по MAC силами ARC (macRediscoverEnabled) — (уже в ARC) да

Безопасность

adb tcpip открывает порт 5555 на самом телефоне (на его Wi-Fi-IP), а не на ПК. Парк из N устройств = N эндпоинтов ip:5555, различаются по IP. Это отладочный доступ к устройству, достижимый для любого узла той же L2-подсети. Доступ всё ещё проходит RSA-авторизацию adb (чужой ключ упрётся в запрос «Allow debugging?» на экране), но сама открытая поверхность + контекст (это банковские приложения) требуют сетевой гигиены. На уровне приложения порт закрыть нельзя — на нерутованном устройстве это свойство adbd, поэтому безопасность здесь почти целиком инфраструктурная:

  • Выделенная/изолированная Wi-Fi-сеть для фермы — без выхода в недоверенный сегмент. На роутере отключить AP isolation (иначе узлы не видят порт 5555 друг друга — это и про работоспособность), но саму сеть держать сегментированной (VLAN/гостевую-наоборот).
  • Защита adbkey управляющего ПК. RSA-ключ этой машины — фактический «пароль» ко всему парку (особенно при «всегда разрешать с этого компьютера»). Хранить только на ней, доступ к машине ограничить.
  • Ограничение на уровне роутера/файрвола — сузить, кто вообще достаёт до :5555 устройств (по возможности — только управляющий ПК).
  • Поиск по MAC / subnetSweepEnabled сканируют подсеть — ещё одна причина держать ферму в выделённой сети (в чужой/общей сети скан нежелателен).

Главный практический долг по безопасности — организационный: выделенная сеть, AP isolation off + сегментация, защита adbkey. В коде ARC тут рычагов нет.

Где в коде

  • android/adb/AdbUtils — низкоуровневые обёртки (isWirelessSerial, getDeviceWifiIp, getDeviceMac, enableTcpip, connectWireless, disconnectWireless).
  • android/adb/WifiAdbManager — авто-bootstrap (tryBootstrap), переподключение (reconnectKnown: починка offline-эндпоинтов через disconnect→connect и поиск по MAC при смене IP), принудительный kick залипшего эндпоинта (kickEndpoint), хранение эндпоинтов (loadEndpoints/forget).
  • services/DeviceScreener — интеграция в цикл опроса (reconnect c починкой stale → dedup USB/Wi-Fi → kick на debounced-offline (с guard по занятости) → bootstrap простаивающих; увеличенный таймаут/интервал screencap для wireless).
  • views/device/DeviceWidget — пассивный бейдж «Wi-Fi» на карточке устройства; показывается только когда устройство онлайн по Wi-Fi (у офлайн-карточки не горит, чтобы не вводить оператора в заблуждение).