14 KiB
ADB через Wi-Fi (TCP/IP)
ARC умеет работать с устройствами не только по USB-кабелю, но и по Wi-Fi (ADB-over-TCP). Включается автоматически, без участия пользователя: USB остаётся основным транспортом, Wi-Fi — горячий резерв, который подхватывается при отключении кабеля.
Поведение
- Пользователь, как и раньше, подключает телефон по USB.
- Фоновый
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"— читается прозрачно).
- читает IP телефона в сети Wi-Fi (
- После этого кабель можно отключить — устройство остаётся онлайн (статус
привязан к
android_id, а не к ADB-serial; меняется толькоadbSerialнаip:port). - При старте ARC и периодически воркер переподключает сохранённые эндпоинты
(
adb connect), так что устройство, у которого кабель уже отключён, само появляется онлайн. - Если беспроводной эндпоинт «завис», воркер чинит его пересозданием TCP
(
adb disconnect→adb 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). Починка пропускается для устройств с активной/ожидающей задачей (см. инварианты).
- эндпоинт виден в
- Если
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 (у офлайн-карточки не горит, чтобы не вводить оператора в заблуждение).