Files
subochev e5d2fa24a1 mercury 0.1.1-SNAPSHOT (fixed enableWirelessAdb URI); B-2 resolved
Phone now self-provisions after a glasses reboot: BLE enableWirelessAdb ->
glasses start wireless adb -> address back over BLE -> adb connect ->
Shizuku started + accessibility/media grants applied. Verified on device
16:22 (glasses rebooted, adb_wifi_enabled=0, nothing activated over USB).
2026-09-27 16:24:33 +03:00

13 KiB
Raw Permalink Blame History

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 не работал — РЕШЕНО

Когда выяснено: 2026-09-27. Симптом: после ребута очков provisioning не проходил — телефон писал «не удалось получить адрес ADB over WiFi на очках», Shizuku на очках не поднимался (хотя Wi-Fi и BLE у обоих были живы).

Настоящая причина — одна и она наша: библиотека rayneo-mercury слала команду не на тот URI.

  1. Очки разбирают входящие кадры по таблице RESPONSE_MAP[uri] (BleResponse из RayNeoLauncher), и debug-ответ зарегистрирован только как "ble.response.debug" → DebugResponse.
  2. Наша Commands.Debug.enableWirelessAdb() слала "ble.request.debug" — это направление очки→телефон.
  3. Итог: кадр доходил до очков и молча выбрасывался в BleServerV2.handleMobileCommand — не совпал ни с "brt", ни с RESPONSE_MAP. Ни обработки, ни ошибки, ни лога. Полный JSON, который уходил: {"uri":"ble.request.debug","payload":{"cmd":"enableWirelessAdb"}}.

Раньше «адрес не приходит» трактовалось как «очки не отвечают» — на деле очки не получали команду, поэтому и адрес было некому слать. (Сам адрес очки шлют назад как ble.request.debug + payload.cmd="WirelessAdbAddress" — это направление корректно и у нас уже разобрано.)

Ground truth — стоковое приложение телефона com.rayneo.mercury: cn.rayneo.mercury.connectivity.util.GlassesDebugCommands.enableWirelessAdb() кладёт в BleResponsePipeline именно DebugResponse(Payload(cmd="enableWirelessAdb")), а у DebugResponse uri == "ble.response.debug". Лишние поля не читаются — обработчик очков (DebugKnife.observeBleWirelessDebugCommand) матчит только payload.cmd и сам вызывает RayNeoAdbService.enableWirelessAdb(true, <текущий ssid>).

Фикс: Commands.Debug.enableWirelessAdb()/disableWirelessAdb() → Uris.RESPONSE_DEBUG; библиотека опубликована как 0.1.1-SNAPSHOT, в rayneo-vm/gradle/libs.versions.toml mercury = "0.1.1-SNAPSHOT".

Проверено на устройстве 2026-09-27 16:22 (очки перезагружены, adb_wifi_enabled=0, по USB ничего не активировал — всё делает телефон):

  1. ble.response.debug → DebugResponse dispatched → RayNeoAdbService: alwaysAllow=true, bssid=O2 5 → open wireless adb success: true → на телефон вернулся адрес 192.168.76.248:41395. Первый ответ бывает ip:-1 (порт ещё не назначен), через ~0.6 c приходит второй, валидный.
  2. Телефон: ShizukuProvisioner: starting server... shizuku_starter exit with 0 → Shizuku поднят (pidof shizuku_server = 4104).
  3. AccessibilityProvisioner включил VmAccessibilityService через тот же adb.

mDNS-fallback в AdbController остаётся как страховка.

Побочный урок: ~/.gradle/gradle.properties содержал левый mercuryVersion=0.1.0-SNAPSHOT, который перебивал проектный, — publish молча уходил в старую версию. Строку убрали.


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».