#pragma once #include #include #include // Автоматическое поднятие/восстановление беспроводного (Wi-Fi, ADB over TCP/IP) // подключения к Android-устройствам. Без участия оператора: USB остаётся основным // транспортом, Wi-Fi — горячий резерв, который подхватывается при отключении кабеля. // // Сохранённые эндпоинты хранятся в реляционной таблице wifi_connections // (WifiConnectionDAO), по строке на android_id: ip/port (= endpoint "ip:port") + mac. // MAC нужен, чтобы найти устройство после смены IP по DHCP (поиск в ARP-таблице). // WifiAdbManager — единственный владелец записи в этой таблице. // // Все методы статические и блокирующие (дёргают adb/arp/ping через QProcess) — // вызывать из фонового потока (DeviceScreener), не из GUI-потока. // Сохранённый беспроводной эндпоинт устройства. struct WifiEndpoint { QString endpoint; // "ip:port" QString mac; // нормализованный MAC (lower-case, разделитель ':'); может быть пустым }; class WifiAdbManager { public: // Параметры одного прохода переподключения сохранённых эндпоинтов. struct ReconnectOptions { QSet alreadyOnline; // android_id, уже видимые онлайн — пропускаем QSet staleEndpoints; // "ip:port" в статусе offline — disconnect перед connect bool macRediscover = false; // при неудаче искать новый IP устройства по MAC bool subnetSweep = false; // разрешить ping-скан подсети для прогрева ARP (медленно/шумно) }; // Сохранённые эндпоинты: android_id → WifiEndpoint. static QMap loadEndpoints(); // Пытается `adb connect` по всем сохранённым эндпоинтам (для устройств, которых // ещё нет онлайн). Возвращает число успешно подключённых. static int reconnectKnown(const ReconnectOptions &opts); // Принудительно пересоздаёт TCP-соединение Wi-Fi-эндпоинта устройства // (disconnect → connect). Нужен, когда соединение «залипло» в полумёртвом // состоянии: adb держит эндпоинт как "device", но команды к нему виснут (устройство // уходит в debounced-offline, при этом в `adb devices` НЕ появляется статус // "offline"). Голый connect в этом случае вернул бы "already connected" и не помог. // No-op, если у устройства нет сохранённого Wi-Fi-эндпоинта (например, офлайн по USB). // Возвращает true, если после переподключения соединение поднялось. static bool kickEndpoint(const QString &androidId); // Bootstrap беспроводного эндпоинта для устройства, подключённого по USB: // снять MAC → tcpip → определить wlan0 IP → adb connect → сохранить эндпоинт. // ВНИМАНИЕ: tcpip рвёт adbd — вызывать только для простаивающего устройства. // Возвращает "ip:port" при успехе, пустую строку при неудаче. static QString tryBootstrap(const QString &usbSerial, const QString &androidId, int port); // Удаляет сохранённый эндпоинт и делает adb disconnect (best-effort). static void forget(const QString &androidId); private: static void saveEndpoint(const QString &androidId, const QString &endpoint, const QString &mac); // Поиск текущего IP устройства по известному MAC (после смены IP по DHCP). // subnetHintIp — последний известный IP (для определения /24 при ping-скане). static QString findIpByMac(const QString &mac, const QString &subnetHintIp, bool allowSweep); static QString arpLookupIp(const QString &normalizedMac); static void pingSweepSubnet(const QString &subnetHintIp); static QString normalizeMac(const QString &mac); static QString ipOf(const QString &endpoint); static QString portOf(const QString &endpoint); };