Files
rayneo-vm/BLOCKERS.md
T
subochev 0f6f07782f UI: развести статусы каналов — сводка «Связь» (BLE / TCP/IP); transport 0.1.1-SNAPSHOT; BLOCKERS B-5
Обе строки назывались «Связь» (Канал 1 WebSocket и Канал 2 Mercury BLE), из-за чего
статус BLE читался как статус TCP: при выключенном Wi-Fi на очках телефон показывал
«подключено». Теперь вверху вкладки «Очки» сводка с отдельными строками BLE (Mercury)
и TCP/IP (Wi-Fi), в секциях — «BLE» и «TCP/IP».

mercuryTransport -> 0.1.1-SNAPSHOT (клиентский WS-пинг, см. BLOCKERS B-5).
2026-09-27 15:56:49 +03:00

158 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Blockers
Сюда пишутся только те задачи/проблемы, в которых застряли. Каждая запись — задача,
что сделано, в чём стена, что проверено, варианты обхода. Записи ведутся по правилам
ночной сессии (см. `TASKS.md`, секция «Правила ночной сессии»).
---
## B-1 · Периодический лаунч `Mercury/.NetConfigActivity` — ПОДТВЕРЖДЁН, источник не найден
**Когда замечено:** 2026-09-27, во время тестов запуска Zona через `DEBUG_LAUNCH`.
**Что я видел в логах:** `ShizukuVdService: launchActivity pkg=com.ffalconxr.mercury.launcher
cls=com.ffalconxr.mercury.launcher.wizard.netconfig.NetConfigActivity displayId=2` повторялось
каждые 5 секунд, постоянно.
**Что это на самом деле:** Непонятно. Это мог быть мой собственный `GlassesDebugReceiver`,
который я перепосылал несколько раз и он где-то зациклился; мог быть артефакт system-job’а;
мог быть какой-то fallback в `VmGlassesController.ensureNetworking()`.
**Повторно замечено:** 2026-09-27 12:28:40, `START ... NetConfigActivity from uid 2000` (shell)
в логах очков во время отладки звука. `uid 2000` = shell ⇒ запускает **Shizuku UserService**
(наш код через Shizuku). Значит источник — где-то в нашем Shizuku-пути (не Mercury BLE).
На аудио-grant влияния не оказал (график grant сломался по другой причине — см. B-3).
**Что НЕ сделано:** не остановил очки в момент пика, не нашёл источник цикла. Пользователь
утром сказал «сейчас вижу наше приложение», значит цикл или закончился, или не повторяется.
**Следующий шаг:** если повторится — переписать GlassesDebugReceiver с rate-limit + проверить
нет ли в `VmService`/`VmGlassesController` периодического `launchActivity(Mercury)` для
Wi-Fi/network fallback.
---
## B-2 · rayneo-mercury: `enableWirelessAdb` через BLE на X3 Pro не возвращает адрес
**Когда выяснено:** 2026-09-27, аудит прошивки RayNeoLauncher по smali
(`adb pull /system/app/RayNeoLauncher`).
**Что происходит (по smali + логам):**
1. Телефон шлёт BLE-команду `ble.request.debug` с `payload.cmd="enableWirelessAdb"`.
2. Очки **принимают** команду и реально поднимают беспроводной adb (мы видим
`adb_wifi_enabled=1` и `service.adb.tcp.port` после). **Это работает.**
3. Очки **должны** прислать событие `WirelessAdbAddress` обратно. **Это не происходит.**
4. В smali `BleResponse` есть `RESPONSE_MAP`, в котором зарегистрированы Wi-Fi / media /
netconfig ответы — но **`DebugResponse` отсутствует**. Поэтому маршрутизация ответа
по URI `ble.request.debug` не настроена, и событие теряется.
5. Телефон вечно ждёт `Event.WirelessAdbAddress`, не получает его и уходит в таймаут.
**Что это значит для provisioning:**
- Команда `enableWirelessAdb` через BLE **включает adb** — но адрес `ip:port` мы узнать не можем.
- Provisioning сейчас работает только потому, что **adb уже был включён раньше** (например,
руками или через shizuku) и не выключился после ребута. После ребута очков — если
`adb_wifi_enabled` сбросится в 0 — provisioning сломается, потому что новый адрес мы
узнать не сможем.
**Обходной путь (уже работает):**
```bash
# Через shizuku shell на очках:
service call adb 4 i32 1 s16 'Caffeine Portable' # включить adb
settings put global adb_wifi_enabled 1 # не дать ему автоотключиться
```
После этого `adb_wifi_enabled` остаётся 1, телефон через mDNS `_adb-tls-connect._tcp`
находит `ip:port` и подключается. Это уже используется как fallback в `AdbController`.
**Следующий шаг:** при первом удобном случае — поднять через shizuku-shell и убедиться,
что provisioning пройдёт полностью через этот путь.
---
## B-3 · Звук end-to-end — РЕШЕНО
**Когда замечено:** 2026-09-27, E2E-проверка после восстановления общей сети «O2 5».
**Симптом:** звук на телефон работал только в первый раз; после `ON_GLASSES` → `ON_PHONE` тишина
и на очках, и на телефоне.
**Корень (найден по логам 2026-09-27):** не сеть и не телефон, а **grant MediaProjection на очках**.
`ensureAudioCaptureLoop` ре-запрашивал проекцию каждые 2.5 c, а `requestProjection()` каждый раз
дёргал `startActivity(MainActivity, NEW_TASK|SINGLE_TOP)`. Система:
```
START com.android.systemui/.media.MediaProjectionPermissionActivity from uid 10090
→ UsageStats event :23 (ACTIVITY_STOPPED) через ~30 мс
→ MainActivity: MediaProjection не выдан: resultCode=0
```
то есть grant-активити гасится, если её запускает только что перезапущенная Activity. Ручной
`KEYCODE_HOME` (уводит нашу Activity с переднего плана) «разблокировал» grant — подтверждало
гипотезу гонки.
**Фикс (вариант A):**
1. `MainActivity` собирает `requestTick` в `repeatOnLifecycle(**RESUMED**)` (был `STARTED`) и флагом
`AudioBridge.activityResumed` сообщает сервису, что она наверху.
2. `requestProjection()` больше **не** вызывает `startActivity(MainActivity)`, пока Activity уже
`RESUMED` (убрана гонка с запуском consent-активити).
3. `ensureAudioCaptureLoop` запрашивает проекцию **один раз**; повтор — не чаще `AUDIO_RETRY_MS=20 c`.
4. Проекция **одна на процесс и не гасится**: `GlassesAudioCapture.stop()` останавливает только
`AudioRecord`, а `AudioBridge.onProjectionGranted` не трогает уже имеющуюся. Следующий `ON_PHONE`
переиспользует ту же проекцию — новый grant не нужен вовсе.
**Проверено вживую 2026-09-27 (очки A06B4AB933C4103 → телефон AXGL024B05001337):**
- `[1] ON_PHONE` → `MediaProjection получен` **без HOME** → захват запущен.
- `[2] ON_GLASSES` → `остановлен (проекцию сохраняю)`.
- `[3] ON_PHONE` → `аудио-захват поднят`, **без нового grant** (переиспользование).
- Тестовый тон 440 Гц: очки `rms=5673`, телефон `playing ... rms=5620..5698`, байты совпали
(`bytes=4751360` с обеих сторон). **PCM реально дошёл по `/vm-audio`.**
**Проверено с руки пользователя 2026-09-27:** полный сценарий с Zona — `ON_PHONE` → `ON_GLASSES`
→ `ON_PHONE` — звук корректно переключается, повторный `ON_PHONE` больше не ломается.
**B-3 закрыт полностью.**
---
## B-4 · Финал: гашение экрана обязательно в конце
По ночным правилам — на завершении всех задач ИЛИ при невозможности продолжать —
выполняется:
```
adb -s A06B4AB933C4103 shell input keyevent 223
```
и проверка `mWakefulness=Dozing/Asleep`. Если не работает — через `ScreenControl` (Канал 1) /
Shizuku `SET_SCREEN`.
**Статус:** выполнено (mWakefulness=Dozing подтверждён в конце ночной сессии).
---
## B-5 · Телефон показывал «подключено» при мёртвом TCP-канале — РЕШЕНО
**Когда замечено:** 2026-09-27, пользователь: «телефон говорит подключено, очки говорят что нет связи».
**Причина:** у очков Wi-Fi был выключен (`wifi_on=0`, нет default network) — реального Канала 1 не
было. Очки при выключении Wi-Fi локально теряют сокеты мгновенно и честно писали «нет связи».
Телефон же об этом не узнавал: FIN/RST до него не доходит (у очков уже нет интерфейса), а у
**клиента `:transport` не было ни пинга, ни таймаута** — `WifiTransport.connect` вечно висел в
`for (frame in s.incoming)`. TCP-сокет телефона оставался в `ESTAB`, `onDisconnected` не срабатывал,
`VmPhoneController` оставался в `Connected` → UI показывал «подключено».
**Доказательство (воспроизведено):** выключили Wi-Fi очкам — через 20 c с телефона:
```
ESTAB 192.168.76.121:50438 → 192.168.76.248:8080
```
сокет так и висел, телефон даже не переподключался (те же local-порты после возврата Wi-Fi).
**Фикс (`rayneo-mercury :transport` 0.1.1-SNAPSHOT):** клиентским транспортам добавлен
`install(WebSockets) { pingIntervalMillis = WS_PING_INTERVAL_MS }` (15 c) — `WifiTransport` и
`WifiByteTransport`; константа в `Transport.kt`. Серверные транспорты пингуют и так
(`pingPeriod=15s`, `timeout=30s`), но они ловят мёртвого клиента, а не наоборот.
**Проверено вживую 2026-09-27:** Wi-Fi очкам выключен → на 10-й секунде у телефона `Send-Q=82`
(пошли пинги, недоставленные) → на ~41-й секунде сокет ушёл в `FIN-WAIT-1` (клиент сам закрыл
мёртвую сессию, `onDisconnected` сработал) → после возврата Wi-Fi новый `ESTAB :57996` и в логах
очков `Phone connected: ws-83ad…` (авто-реконнект).
**Побочно:** в UI телефона обе строки назывались одинаково — «Связь» (Канал 1 WebSocket и
Канал 2 Mercury BLE), из-за чего статус BLE читался как статус TCP. Теперь вверху вкладки «Очки»
сводка «Связь» с отдельными строками **BLE (Mercury)** и **TCP/IP (Wi-Fi)**, а в секциях каналов
строки переименованы в «BLE» и «TCP/IP».