From b84a6ab3b99a1eb964cd7e5269d632344c8d108e Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 26 Oct 2026 18:17:11 +0100 Subject: [PATCH] docs(call): record validated Bluetooth lifecycle fix and regressions --- doc/call_audit_findings.txt | 127 +++++++++++++++++++++++++++++++++++- doc/tasks.md | 13 ++++ 2 files changed, 139 insertions(+), 1 deletion(-) diff --git a/doc/call_audit_findings.txt b/doc/call_audit_findings.txt index 31927357..50de759e 100644 --- a/doc/call_audit_findings.txt +++ b/doc/call_audit_findings.txt @@ -25,7 +25,11 @@ desktop-профиль25/25 PASS; подтверждён дефект Android mu Новая регрессия5/5, voice/desktop30/30, Android timing/trace14/14, assembleDebug для двух ABI и check203/203 PASS,proxy9/9,burst1/1,load8/8,168с. Диагностика/план — раздел13, внедрение/актуальная проверка — раздел14. -База проверки: f79b9261, чистое рабочее дерево после предыдущего push stream. +Продолжена проверка Android playback/router. После отдельного согласования +плана исправлено владение Bluetooth proxy при позднем callback, disconnect +и restart; постоянные JVM-регрессии15/15, прежний профиль26/26, +voice30/30, сборки и check204/204 PASS. Подробности — раздел15. +Исходная база аудита: f79b9261, чистое дерево после предыдущего push stream. 1. Подтверждённый дефект: call_start не проверяет обычную занятость @@ -1036,3 +1040,124 @@ IPv6/lo включены только в отдельном sudo unshare --net; запускались. Реальная акустика/переключение mute на устройстве требуют отдельного контроля; непосредственное последействие lossy Opus-декодера этим тестом не измеряется. Новые коммиты локальные, push в этом шаге не выполнялся. + +15. Android I/O: владение Bluetooth profile proxy и проверка исправления + +База этого продолжения — опубликованный fa76447f, после rebase Android mute. +Прочитаны CallAudioEngine/CallAudioOutput, playback clock/buffer/recovery, +CallAudioRouter, владельцы в CallController/MediaAudioController и native +android_call_output_jni.c. Это продолжение платформенной проверки голоса; +видео и оставшиеся core/platform контракты целиком не закрыты. + +Подтверждённый дефект и SDK-контракт + +Прежний router принимал onServiceConnected без проверки started/поколения. +stop до первого callback оставлял принятый Android proxy открытым; +disconnect обнулял btHeadset/btProfileProxyBound, хотя владение сохранялось; +при stop/start старый callback мог подменить proxy нового lifecycle. +При последующем connected новый proxy перезаписывал ссылку на старый. + +Сверены локальные исходники установленного Android SDK36: +BluetoothAdapter.java:884–931,3412–3491,3495–3539,3690–3740; +BluetoothHeadset.java:498–512 в /home/user/android/sources/android-36/android/bluetooth/. +getProfileProxy регистрирует proxy до доставки callback через main Handler. +Отключение сервиса/Bluetooth сохраняет регистрацию до closeProfileProxy; +закрытие идемпотентно, getConnectedDevices без сервиса возвращает пустой список. +MediaAudioController действительно вызывает router.start из I/O executor, +поэтому одного сравнения поколения вне общей критической секции недостаточно. + +Сначала без изменений проекта проверены два штатных контроля PASS и три +ожидаемых FAIL старого router: late, disconnected, pending_restart. +Временная модель в /tmp/call-router-audit управляет порядком API callbacks; +читает приватное состояние только для диагностики, не подставляет его. +Кандидат /tmp/call-router-audit/CallAudioRouterCandidate.kt:9/9 PASS, +включая64 конкурентных callback/stop; после cleanup нет открытых proxy. +Компиляция кандидата с настоящими типами SDK36 PASS; прежний Android +timing/trace/buffer/recovery профиль26/26 PASS. Проект/документы/чек-лист +на этапе обсуждения плана не менялись, новых коммитов не было. +/tmp/call-router-plan-candidate.diff, /tmp/call-router-plan-{build,results,sdk-build,validation}.log; +исходные логи /tmp/call-router-audit-*.log. + +Согласованный план и внедрение + +По запросу пользователя план сначала изложен в чате и проверен на временном +кандидате; после следующего «ok» внедрён в production CallAudioRouter: +1) btHeadset означает владение; btProfileAdapter хранит создавший adapter. + Удалён дублирующий флаг btProfileProxyBound. started/proxy имеют volatile + для читателей вне монитора; adapter/поколение используются под монитором. +2) start, уже синхронизированный stop и оба BT callbacks используют один + существующий монитор router. Новых потоков и объектов lock нет. +3) Запрос захватывает поколение/adapter. Текущий connected принимает proxy; + поздний connected закрывает его через захваченный adapter, не меняя owner. +4) Текущий disconnected сохраняет ссылки и обновляет доступность устройств; + старый disconnected игнорируется. Reconnect того же proxy разрешён. +5) stop инвалидирует поколение до раннего return, отсоединяет ссылки до + Android close и закрывает также proxy отключённого сервиса. Повтор stop + не закрывает тот же owner повторно. Пока pending-запрос не дал proxy, + публичный API не позволяет закрыть его: очистка происходит при connected. + +Логи CallAudioRouter содержат generation/current, proxy, accepted, +connected/retained/owned closed/stale closed/ignored. Отказ запроса и API +exceptions имеют WARN/ERROR; успешный media hot path не получил новых логов. +Сигнатуры публичного router, wire, JNI, DSP и алгоритмы playback не менялись. + +Постоянная регрессия и отрицательный контроль + +Добавлен отдельный Kotlin/JVM Gradle-модуль tools/chatgui-android/router-tests. +Он компилирует реальный CallAudioRouter.kt приложения с минимальными моделями +Android API. Поля router не подменяются и не читаются через reflection; +критерии — публичная доступность/route, callbacks, API close и реестр proxy +на стороне fixture. Модель не создаёт системных Bluetooth/Binder-ресурсов. +JUnit15/15 PASS, failures0/errors0/skipped0: +- connected/stop, два штатных restart, duplicate start и повтор stop; +- late connected после stop, disconnect/stop, pending stop/start; +- reconnect одного proxy, старый disconnected после принятия нового proxy; +-64 конкурентных callback/stop с join и проверкой ownership после каждого; +- I/O start, задержанный внутри getProfileProxy барьером, одновременные + callback/stop: все потоки завершаются, proxy/device callback освобождены; +- rejected request, exception, отсутствие adapter, отзыв permission после acquire. +Все положительные cleanup оставляют0 proxy и0 зарегистрированных device callbacks; +неожиданных ERROR нет. Два WARN намеренно проверяют rejected/throw request. + +Обычная задача :app:testDebugUnitTest зависит от :router-tests:test только +через зависимость задач. JVM fixtures не включены в classpath приложения/APK. +JUnit используется той же версии4.13.2, Kotlin1.9.20; новых внешних библиотек нет. +Параметры routerSourceDir/routerTestsBuildDir позволяют независимый старый +исходник/выход сборки в /tmp без замены production файлов или положительных результатов. +С router из fa76447f: штатный connectedStop PASS, late/disconnected/pendingRestart +дают ровно три ожидаемых FAIL из четырёх тестов, Gradle exit1. +Каждый FAIL показывает оставшееся владение; @After очищает только объекты +fixture. Это проверка чувствительности к исходному дефекту, не сбой исправления. +/tmp/call-bt-fix-negative.log и /tmp/call-bt-fix-negative-build/test-results/test/. +Положительный и отрицательный вывод новых сценариев прочитан полностью. + +Сборка и общий профиль + +./build.sh --no-gui -j4 PASS. Android Gradle одним запуском выполняет +:router-tests:test, :app:testDebugUnitTest для Timing8/Trace6/PlaybackBuffer10/ +OutputRecovery2 и assembleDebug:15+26 PASS, BUILD SUCCESSFUL13с. +APK содержит arm64-v8a/x86_64 libutun.so, fixture-маркеров в DEX нет. +Известное предупреждение AGP8.2 о compileSdk36 и четыре unused-параметра +моделей API не являются ошибками сборки. APK не устанавливался. +После уточнения только комментария снимка устройств финальный assembleDebug +также PASS,1с; поведение router после положительных тестов не менялось. +/tmp/call-bt-fix-{native-build,android-build,android-final-build,gradle}.log; +JUnit XML в router-tests/build/test-results/test и app/build/test-results/testDebugUnitTest. + +CMake configure/build и voice/desktop30/30 PASS,20,41с. Включены прежние +тесты mute/TX/часов/очередей/восстановления. Эти тесты не выполняют Bluetooth. +/tmp/call-bt-fix-voice-{configure,build,tests,details}.log. +Полный ./check.sh:204 passed,0 failed,0 skipped,exit0,173с; +proxy9/9,burst1/1 (8388608 байт),load8/8 (134217728 байт), обе стороны rc0. +JVM-регрессии15 и CMake voice30 в число C-only204 не входят. +/tmp/call-bt-fix-{full-check,check-runner}.log и {check,netns}.sh. +Весь общий вывод прочитан. IPv6/lo включались только в отдельном network +namespace; check работал от user. Host all/default/lo disable_ipv6=1 сохранены. + +Задача закрыта, doc/chat_call_arch.md обновлён: BT ownership, AAudio20мс/ +AudioTrack40мс, reporter resize и переоткрытие выхода playback worker, +сохранение незаписанного хвоста и база общего счётчика без обещания replay +прежней принятой устройством очереди. Runtime playback этим исправлением не менялся. +Общий аудит остаётся частичным:call0[V]/11[~]/10[ ],desktop8[~],архитектура[~],видео[ ]. +Настоящие Binder callbacks, акустика/BT на телефоне, новый sanitizer и +Windows/FreeBSD runtime не проверялись. Коммиты локальные, push не выполнялся. diff --git a/doc/tasks.md b/doc/tasks.md index 0ddda15d..3c2d9175 100644 --- a/doc/tasks.md +++ b/doc/tasks.md @@ -33,6 +33,19 @@ Подробности: [dm_audit_findings.txt](dm_audit_findings.txt),раздел9; /tmp/dm-utf8-fix-*.log. Исправление закрыто; полный аудит DM остаётся частичным, аппаратные GUI/Android не проверялись. +[+] **Android CallAudioRouter: владение Bluetooth proxy при stop/restart** — исправлено 26.10.2026. + Поколение запроса и общий монитор start/stop/BT callbacks исключают принятие proxy старого lifecycle. + Disconnect сохраняет owner до close; stop отсоединяет ссылки и закрывает через создавший adapter, + поздний connected закрывает свой proxy. Диагностика содержит proxy/generation/accepted и ошибки API. + Отдельный JVM-модуль компилирует production router с управляемой моделью Android API:15/15 PASS, + включая64 конкурентных callback/stop и I/O start с задержанным callback; после cleanup0 proxy/callbacks. + Прежний исходник:штатный контроль PASS,три ownership-сценария ожидаемо FAIL. + Android clock/trace/buffer/recovery26/26,voice/desktop30/30,нативная сборка и assembleDebug для двух ABI PASS. + check.sh204/204 PASS,0 failed/0 skipped;proxy9/9,burst1/1,load8/8,exit0,173с. + Fixtures не входят в APK;Bluetooth/Binder/звук на устройстве не проверялись. Общий аудит call остаётся частичным. + Подробности: [call_audit_findings.txt](call_audit_findings.txt),раздел15; + /tmp/call-bt-fix-{gradle,negative,voice-tests,full-check}.log. + [+] **Android call mute: настоящий PCM и флаг mute передаются через JNI до core** — исправлено 26.10.2026. Kotlin передаёт исходный buf и snapshot muted на кадр; NativeLib/JNI/C bridge вызывают feed_pcm_muted. AEC/AGC продолжают получать микрофон, mute зануляет PCM непосредственно перед Opus; wire/DSP/desktop не менялись.