Files
kk0t9 3fd9b42bb0 docs(tails): закрыть кросс-док хвосты (a-base/b/dev-env/J/hardware) + наполнить glossary
Закрыты накопленные мелкие хвосты из ревью F/J/H/G:
- Stage-нормализация: Stage-0/1/2 → Stage 0/1/2 по 6 докам (a/b/f/h/j/hardware); каноническая запись Stage 0/1/2 в glossary.
- a-base §8: видео-пайплайн (DMABUF камер / VPU dashcam) внесён в OOM-порядок — задняя защищена (Stage 1), dashcam/surround throttleable.
- a-base §12: dashcam-медиа (отдельный носитель) + контакты/журнал (G) в список factory-reset wipe.
- b §12: grace-hold резолвнут  — J запросчик (J §7), B владелец/арбитр (§4 шаг 2, §7).
- dev-environment: моки fake-камера (J)/аудио (H)/BT-телефон (G) + plugin-host-харнесс; just-цели plugin-dev-run/sideload.
- J §3/§11/§13 + hardware §4: сигнал реверса  GPIO фонаря з.х. (выбранный дефолт); CAN-gear отложен (нет gear-сигнала в E).
- glossary.md: наполнен (~55 терминов в 7 областях: машина/CAN, платформа/IPC, ассистент/аудио, питание/boot, хранилище/OTA, связь/телефон, безопасность).

Tier-3 capability-catalog + roadmap не трогаются — зависят от доменов I+L.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 12:46:36 +03:00

256 lines
28 KiB
Markdown
Raw Permalink 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.
# Домен H — Медиа / аудио
> Вся стандартная мультимедиа (локальное аудио, BT A2DP/AVRCP, радио, стриминг) **и**
> сквозной **арбитр аудио** — политика фокуса/ducking поверх PipeWire, которой подчиняются
> ассистент, телефон, навигация и служебные звуки. First-party ап на SDK (Rust).
Статус: **v2 (на ревью).** v2 — после adversarial-ревью (20 находок).
Связано с: [architecture.md](../architecture.md) (§3 Media, §4 data-plane, §5 карта связей, §9 аудио-арбитраж) · [ipc.md](../contracts/ipc.md) (`Media`, `Assistant`, `Phone`, `Power`) · [security-privacy.md](../contracts/security-privacy.md) (§3 `audio_out`/`audio_in`, §4 гейтинг) · [plugin-sdk.md](../contracts/plugin-sdk.md) (§3 `network`, §4 точки расширения) · [hardware.md](../contracts/hardware.md) (§4 codec/усилитель/mic/тюнер/amp-mute, §3 load-shedding) · [tech-stack.md](../tech-stack.md) (PipeWire/WirePlumber, `symphonia`) · домены **D** (TTS/AEC), **G** (HFP/BT/проекция), **I** (нав-промпты), **C** (мультируль/слоты/distraction), **B** (lifecycle), **J/E/K** (служебные звуки) · [principles.md](../principles.md) (#3,#4,#6,#8,#11,#12,#13)
---
## 1. Назначение и границы
- **Две роли:** (1) **медиаплеер** — источники звука (локальные файлы, BT-аудио, радио, стриминг) +
now-playing/транспорт; (2) **арбитр аудио** — системная политика фокуса/ducking, по которой звук
ассистента/телефона/навигации/служебных сигналов сосуществует с медиа.
- **H — design-owner аудио-политики**, но *enforcement* живёт в **WirePlumber** (база, см. §3) — не в
процессе плеера: падение плеера (#4) не должно ломать маршрутизацию звонка/ассистента.
- **Границы:** H не владеет BT-адаптером/паррингом (домен G/Connectivity; H цепляет только A2DP/AVRCP — §6)
и не генерирует служебные звуки сам — их эмитит владелец фичи (park-beep — иллюстративный пример;
**владелец синтеза служебных звуков открыт, §15**); H лишь **даёт им приоритет роли** `alert`. Не
safety-critical (#1): служебные звуки — информирование, не управление, без регуляторных claim'ов.
- **Plane:** звук — **PipeWire** (+ WirePlumber), data-plane; control/состояние — лёгкий D-Bus (`Media`, §11).
## 2. Аудио-плоскость: роли потоков
- Каждый продюсер звука тегирует свой PipeWire-поток двумя **ортогональными** ключами: **`media.role`**
(use-case — **ОСНОВА арбитража** §3: `media`/`nav_guidance`/`assistant_tts`/`phone_call`/`projection`/
`alert`/`emergency`-резерв) и **`media.category`** (`Playback`/`Capture` — тип обработки, в арбитраже
НЕ участвует; микрофон ассистента — `Capture`, см. §4).
- Роль (`media.role`) — **единственное, что знает политика**: арбитраж по роли, не по «кто это» → развязка
(#4,#9). first-party теги доверенные; стороннему плагину доступен только `media` (через `audio_out`, §11).
- Высокочастотного звука по D-Bus нет (architecture §4): D-Bus несёт лишь состояние/команды.
## 3. Политика фокуса и ducking (арбитр — ядро H)
**Лестница приоритетов playback-ролей** (`media.role`; выше вытесняет ниже):
| Роль | Приоритет | Действие над нижними |
|------|-----------|----------------------|
| `alert` (park-beep, предупреждение) | высш. | короткий **mix-over** (+ duck медиа на длительность) |
| `phone_call` / `projection`-звонок (G) | высокий | **cork** (пауза) медиа; нав-промпт — duck/очередь |
| `assistant_tts` (D) | выше-средн. | **duck** медиа; нав-промпт — очередь |
| `nav_guidance` (I) | средний | **duck** медиа на время промпта |
| `media` (файлы/BT/радио/стриминг) | базовый | — |
*(`assistant_capture` — это `media.category=Capture` (микрофон+AEC §4), а НЕ строка output-лестницы:
capture-нода не «дакает» вывод; дакает медиа именно playback-роль `assistant_tts`.)*
- **Cork vs duck:** cork = пауза с авто-resume по освобождению; duck = временное понижение громкости с
восстановлением. Звонок **корчит** медиа (не микширует); ассистент/нав — **дакают**. **duck —
относительный аттенюатор** поверх текущего пользовательского уровня (master/per-stream), НЕ запомненное
абсолютное значение: изменения громкости мультирулём (§8) во время duck применяются к базовому уровню и
**переживают** un-duck (restore снимает аттенюатор к *текущему* уровню, не к уровню на старте duck); при
`mute`/master=0 duck/un-duck — no-op.
- **Intra-role `media` (единственный media-фокус):** одновременно активен ровно один продюсер роли `media`;
появление нового (плагин-media-source §7, BT-A2DP §6, локальный плеер §5) **корчит** предыдущего держателя
(audio-focus-семантика), а не микшируется (микс в пределах роли — только для `alert`). Координирует
динамическая часть Media-апа (§11 `SetSource`/состояние фокуса). (Ср. арбитраж 0..N видео-источников J §5.)
- **Конкуренция transient:** `assistant_tts` > `nav_guidance` (нав в очередь). Во время звонка медиа
закорчено, ассистент подавлен; `alert` — микшируется поверх всего (короткий, слышен даже в звонке).
- **Гистерезис фокуса (анти-pumping):** переходы duck/cork по `Assistant.StateChanged` сглаживаются —
минимальное окно удержания (hold) перед un-duck (debounce серии idle↔listening от ложных wake-word/VAD,
ср. D §2) + восстановление громкости **фейдом** (release), не скачком. Конкретные attack/hold/release —
реализация (§15 split-политики).
- **Crash-safe restore (две оси):**
- *source-ось:* duck/cork привязаны к **жизни потока-ноды** — исчез продюсер (упал ассистент/нав) →
WirePlumber восстанавливает медиа (#4, ср. ipc §2). **Проактивный cork** (медиа корчится по сигналу
`Phone` ДО появления ноды `phone_call`) на верхнюю ноду не завязан, поэтому его откат гарантируют
дополнительно: (1) исчезновение владельца имени Media-координатора → снятие наложенных корок по
`NameOwnerChanged` (ipc §2); (2) watchdog-timeout, если нода звонка не материализовалась за T мс
(отбой до connect) → авто-un-cork.
- *sink-ось:* при исчезновении output-ноды (load-shed усилителя, hardware §3) наложенные cork/duck —
невалидны относительно этого выхода; при **возврате** output-ноды (напр. после `ShutdownAborted`, B §4)
политика **пересчитывается заново** по живым ролям-продюсерам, а не наследует залипшее состояние.
- **Где живёт:** *статическая* лестница ролей — **конфиг WirePlumber** в базовом образе (поднимается на
**Stage 1**, §9; переживает крэш плеера); *динамическая* координация (проактивный cork, гистерезис,
intra-role, состояние для UI) — компонент Media-апа, подписан на `Assistant.StateChanged`/`Phone`/`Nav`/
`Power` (ipc §34). До Media-апа (v1) хватает статической политики + ролей ассистента.
## 4. AEC и loopback-референс *(резолв D §10)*
- H **публикует monitor/loopback-tap смешанного выхода** как референс для эхоподавления (без него
wake-word во время воспроизведения и barge-in невозможны).
- **AEC — узел audio-графа на базе `module-echo-cancel`** (WebRTC APM, `libspa-aec-webrtc`, BSD — #12) на
пути capture микрофона; референс — monitor-порты выхода. **Не внутри ассистента** (D потребляет уже
очищенную capture-ноду). *(`module-echo-cancel` — отдельный модуль PipeWire, не `module-filter-chain`:
последний — иной механизм (граф LADSPA/LV2/builtin), пригоден лишь для опц. кастомного денойза.)*
→ закрывает «место AEC (в audio-plane vs внутри ассистента)» из D §10 в пользу audio-plane.
- Beamforming/денойз салона (D §10) — отдельно (mic-массив, hardware); AEC ≠ денойз.
## 5. Локальное аудио
- **Библиотека** с USB/`/data`: индексация тегов (artist/album/track/обложка), плейлисты, очередь.
Индексация — **фоном/лениво** (#11), не блокирует UI; БД индекса в приватном хранилище апа.
- **Декодер:** 🟡 **`symphonia`** (чистый Rust: MP3/AAC/FLAC/Vorbis/WAV/ALAC; MPL-2.0 — допустимо по #12).
MP3/AAC/ALAC — за **не-дефолтными фич-флагами**; **AAC** — патенты живы (роялти за декодер) → юр-проверка
перед включением (#12); MP3-патенты истекли (2017). **Opus у `symphonia` НЕ декодируется** → для `.opus`/
Opus-в-`.ogg` (и Opus-интернет-радио §7) — отдельный биндинг libopus **`audiopus`** (BSD, #12). → tech-stack.
- **Gapless/очередь:** декодер-пайплайн/очередь предусматривают **предзагрузку следующего трека** на стыке
(gapless — для альбомов/концертников; crossfade — опц. поверх). Включаем ли — §15.
- **Отказ-пути (симметрично J §3/§4):** битый/недекодируемый файл → **skip трека** + неблокирующая индикация
(не стоп очереди); **ENOSPC при индексации / исчезновение USB-носителя** → gracefully остановить индексацию/
трек с видимой индикацией, не крэш (ср. J §4 «носитель отсутствует → стоп с индикацией»).
- Офлайн (#3): локальное аудио и радио не зависят от сети.
## 6. Bluetooth-аудио (A2DP sink + AVRCP)
- **A2DP sink:** телефон → головблок играет звук телефона. Кодеки: **SBC** (обяз., свободный) + **AAC**;
**aptX/LDAC — проприетарны/лицензируемы → по умолчанию ИСКЛЮЧЕНЫ** (#12), опц. позже при лицензии.
- **AVRCP:** метаданные (трек/исполнитель/позиция) + транспорт (play/pause/next с головблока → телефон).
- **Обрыв A2DP в середине трека** (телефон ушёл из зоны / disconnect-событие от G/Connectivity) →
**cork/пауза транспорта + auto-resume по реконнекту**, не крэш, видимая индикация.
- **Шов с G/Connectivity:** BT-адаптер, паринг и профиль **HFP** (звонок) — **не H** (домен G/Connectivity).
H цепляет A2DP/AVRCP к уже спаренному устройству. Сосуществование A2DP↔HFP на одном устройстве — через
политику (§3): входящий `phone_call` корчит A2DP-`media`. Владелец BlueZ-адаптера/паринга —
Connectivity-core (домен G, резолв G §3); сосуществование A2DP↔HFP — политика §3.
- **Проекция (CarPlay/Android Auto)** несёт свои медиа+звонок — её аудио идёт ролями `projection`/`phone_call`;
контроль/протокол проекции — **G**. H лишь маршрутизирует звук по политике.
## 7. Радио, стриминг, плагин-источники *(later)*
- **FM-радио (+ RDS):** 🟡 **требует тюнера, которого нет в hardware §4** → добавить FM-тюнер в периферию
**или** отказаться от радио (§15 → hardware). DAB для РФ — вне скоупа. RDS-station/TMC — задел (TMC мог бы
кормить нав I). Фаза v3.
- **Стриминг:** провайдер-агностично (#8), бэкенды сменные; **креды — свои у пользователя** (как LLM в
D §5). RU-сервисы (Yandex Music/VK/Звук). **DRM/ToS** некоторых сервисов — open (§15). Сеть — через
`network`-capability (plugin-sdk §3, host-allowlist). Underrun/исчерпание буфера → `PlaybackState=buffering`
(§11) с rebuffer + таймаут → стоп с индикацией (ср. online-degradation D §5). Фаза v3.
- **Интернет-радио** (HTTP/Icecast-потоки) — лёгкий вход, только `network` (Opus-потоки → `audiopus`, §5). Фаза v3.
- **Источники-как-плагины (#9):** H даёт точку расширения «media-source» — плагин регистрирует источник
звука (стриминг/подкасты/интернет-радио), как J — 0..N видео-источников. `audio_out`+`network`; подчиняется
intra-role media-фокусу (§3). → F/plugin-sdk.
## 8. Вывод: громкость, маршрутизация, усилитель
- **Выход:** PipeWire → I2S codec → усилитель (hardware §4). Одна зона салона (MVP); мульти-зона
(задние) — вне скоупа/later (§15).
- **Громкость:** master + per-stream; mute; анти-clipping лимитер на миксе. 🟡 **Кривая — перцептивная
(логарифмическая)** дефолт (линейная даёт неюзабельную ручку в шуме салона); реализуется в
PipeWire-graph/WirePlumber (→ tech-stack). Balance/fader (L/R) и эквалайзер — вне скоупа MVP (одна зона), задел later (§15).
- **Два тракта команды громкости** (разные каналы, одно действие): (1) физические кнопки **мультируля**
приходят как key-события через uinput → libinput → Wayland-input сфокусированному Media-клиенту (C §9,
ADC-лесенка/проксированный CAN — K §3) — это **НЕ интент ассистента**; (2) голосовое «громче/тише» —
локальный интент D §6 (после STT, без LLM).
- **Pop-suppression:** усилитель **mute при boot/shutdown** (anti-pop); enable/mute-секвенс amp — владелец
физической линии **hardware §4** (mute-GPIO), владелец порядка **B §4** (mute-then-cut в teardown). При
power-loss усилитель **сбрасывается load-shedding'ом** (hardware §3) — H обязан корректно отработать
исчезновение output-ноды (без зависания, §3 sink-ось), не пытаться «доиграть».
## 9. Медиа и lifecycle (шов с B)
- **Boot аудио-плоскости:** PipeWire+WirePlumber и статическая лестница ролей (§3) поднимаются на **Stage 1**
(раньше Media-апа; сам Media — Stage 2, architecture §6), чтобы маршрутизация/политика были доступны до
прогрева плеера. **До Stage 1 звука нет** (как голоса нет в boot-окне, D §8). Роль `alert` доступна с
поднятия WirePlumber (Stage 1); **ранний park-beep на Stage 0-реверсе** (до аудио-плоскости) — за её
пределами, потребует отдельного раннего звукового пути (◻️ §15 → J/E/B; park-beep informational, #1).
- **`ShutdownImminent`/ACC-off (ipc §3, B §4):** остановить воспроизведение, **mute amp до PONR** (порядок —
B §4, §8), сохранить now-playing (источник/трек/позиция/очередь) в Settings.
- **Resume после рестарта (later):** восстановить контекст **без авто-старта**, если источник недоступен
(BT ещё не переподключился §6, USB вынут, нет сети §7) — показать now-playing как `paused`/`unavailable`
(§11) и ждать появления источника; авто-play — только когда источник готов; **не висеть в `buffering`**.
- **Sleep:** аудио выключено; медиа **не держит систему бодрствующей** вопреки power-политике (#5, B).
Воспроизведение — в `running`/`accessory`, не в `sleep`/`battery_cutoff`.
- **Память (a-base §8):** буферы декода/стриминга малы vs видео; медиа — throttleable, низкий приоритет OOM.
## 10. Driver-distraction (#6, владелец — C)
- На скорости выше порога: now-playing **упрощается**, просмотр библиотеки/набор текста — блокируются/
сворачиваются; приоритет — голосовому управлению (D). Правило — единое через Shell/SDK (C §7), H подчиняется.
## 11. IPC / интерфейсы
- **`ru.shturman.Media`** (ipc §4, домен H): `Play()`/`Pause()`/`Next()`/`Prev()`/`Seek(pos)`,
`SetSource(id)`, `ListSources() → [(id, kind)]` (включая плагин-media-source §7, не только встроенный
плеер); сигнал `NowPlayingChanged`; properties `PlaybackState` (`stopped`/`playing`/`paused`/`buffering`/
**`unavailable`** — источник пропал/не готов), `NowPlaying` (метаданные), `Volume`.
- **Аудио-политика:** наружу — **состояние** (`AudioFocusChanged(role)` / property `ActiveFocus`,
`DuckingActive`) для UI; запрос фокуса — **в основном неявно** (роль потока + WirePlumber). Явная
D-Bus-форма focus-request — ◻️ при необходимости (§15).
- **Гейтинг:** воспроизведение плагином — `audio_out` (security-privacy §3: только playback-ноды, не
capture/monitor чужих); микрофон/loopback-монитор — **никогда** стороннему по `audio_out` (это `audio_in`).
- **UI:** now-playing — декларативный тайл (plugin-sdk §4.1) + богатый медиа-экран как **Wayland-поверхность**
в слоте shell (C §4, slot-протокол).
## 12. Dev-симулятор (#13)
- **Fake-аудио-источники** поверх PipeWire (dev-environment): синтетический медиа-поток, мок BT-A2DP-ноды,
мок нав-промпта/TTS/звонка для матрицы ducking (§3); мок исчезновения output-ноды (load-shed / sink-ось
restore); **отказ-сценарии §5/§6/§7** (битый файл, обрыв A2DP, ENOSPC, stream-underrun); дребезг
`StateChanged` (гистерезис §3). Сценарии «медиа↔звонок↔ассистент» и «два media-источника» (e2e) — отсюда. → dev-environment.
## 13. Функции
| функция | MVP/later | зависит от | фаза |
|---------|-----------|------------|------|
| Аудио-плоскость + роли (PipeWire `media.role`, Stage 1) | **MVP** | PipeWire/WirePlumber | v1 |
| Политика фокуса/ducking (лестница, crash-safe 2 оси, гистерезис) | **MVP** | WirePlumber, D (state) | v1 |
| AEC (`module-echo-cancel`) + loopback-референс | **MVP** | PipeWire, D | v1 |
| Громкость/mute (master + per-stream, кривая, мультируль) | **MVP** | C/K (ввод) | v1 |
| Вывод → codec/усилитель + pop-suppression | **MVP** | hardware, B | v1 |
| Локальный плеер (библиотека + декодер + отказ-пути) | **MVP** | storage, `symphonia`/`audiopus` | v2 |
| Now-playing UI + транспорт (тайл + поверхность) | **MVP** | C (слоты), Shell | v2 |
| BT A2DP sink (SBC/AAC) + AVRCP (+ обрыв-handling) | **MVP** | BT-адаптер (G/Connectivity) | v2 |
| Resume playback после рестарта (без авто-старта) | later | Settings, B | v2 |
| Gapless-воспроизведение | later | `symphonia` | v2 |
| FM-радио (+ RDS) | later | **тюнер (нет в hardware §4)** | v3 |
| Стриминг (провайдер-агностик, креды юзера) | later | network, plugin-sdk | v3 |
| Интернет-радио (HTTP/Icecast) | later | network | v3 |
| Media-source точка расширения (плагины) | later | plugin-sdk, F | v3 |
| Мульти-зона / balance-fader-EQ / BT A2DP source | later | hardware/BT | later |
## 14. Зависимости
- **Вниз:** hardware (§4 codec/усилитель/mic/тюнер/amp-mute-GPIO, §3 load-shedding), PipeWire/WirePlumber +
`module-echo-cancel` (tech-stack), storage (`/data`, библиотека), `symphonia`/`audiopus`/WebRTC-APM (tech-stack).
- **Вбок:** **D** (роли `assistant_tts`/`capture`, AEC-потребитель, `StateChanged`), **G** (HFP/BT-паринг/
проекция — звонок корчит медиа), **I** (нав-промпты, роль `nav_guidance`), **B** (ShutdownImminent/sleep/ACC,
load-shed, amp-mute-порядок), **C** (мультируль-ввод, слоты now-playing, distraction-owner), **J/E/K** (служебные `alert`).
- **Вверх:** медиа-UI и состояние фокуса для Shell; расширяемо плагинами (`audio_out` + media-source).
## 15. Открытые вопросы
- 🟡 **Декодер/кодеки:** `symphonia` (рек.) + **AAC-патент** (роялти) — юр-проверка; Opus → `audiopus`; SBC/AAC
по BT, **aptX/LDAC исключены** (#12). → tech-stack.
- 🟡 **FM-тюнер:** добавить в hardware §4 периферию **или** отказаться от радио. → hardware.
-**Владелец BlueZ/паринга** — Connectivity-core (резолв в G §3); сосуществование A2DP↔HFP — политика §3. → G.
- 🟡 **enable/mute-секвенс усилителя (anti-pop):** owner GPIO — hardware §4 (mute-GPIO), owner порядка — B §4. → hardware + B.
-**AEC** — узел audio-plane (`module-echo-cancel`/WebRTC APM), референс = output-monitor, не в ассистенте;
loopback-tap — §4. → резолв §4 (синхронизировано в d-assistant §10).
- ◻️ **Ранний звук на Stage 0-реверсе** (park-beep до поднятия аудио-плоскости) — нужен ли отдельный путь. → J/E/B.
- ◻️ **Split политики:** сколько в статическом WirePlumber vs Rust-координаторе (+ crash-safety проактивного
cork, тайминги гистерезиса/фейда дакинга). → реализация.
- ◻️ **Явный focus-request API** (D-Bus) vs только неявные роли. → реализация/ipc §4.
- ◻️ **Gapless/crossfade** — включаем ли в локальный плеер (предзагрузка трека). → реализация (v2).
- ◻️ **Стриминг:** какие RU-сервисы, **DRM/ToS**, официально vs плагин. → later.
- ◻️ **Генерация служебных звуков** (park-beep/предупреждения): ассеты и владелец синтеза (J/E vs общий chime-сервис H). → J/E + H.
- ◻️ **Мульти-зона** (задние) + balance/fader + EQ — подтвердить «вне скоупа MVP». → later/hardware.
---
## Журнал решений (домен H)
| Решение | Выбор | Дата |
|---------|-------|------|
| Роль H | медиаплеер + **design-owner аудио-политики**; enforcement в WirePlumber (не в плеере) | 2026-06-22 |
| Модель арбитража | `media.role`-теги (playback) + лестница; `media.category` (Playback/Capture) — не арбитраж; intra-role media — единственный фокус | 2026-06-22 |
| Ducking | cork (звонок) vs duck (ассистент/нав); duck — относительный аттенюатор; гистерезис/фейд (анти-pumping) | 2026-06-22 |
| Crash-safe | две оси: source (жизнь ноды + проактивный cork по `NameOwnerChanged`/watchdog) + sink (пересчёт при возврате output) | 2026-06-22 |
| Где политика | статика — конфиг WirePlumber (Stage 1, переживает крэш); динамика — координатор Media-апа | 2026-06-22 |
| AEC | `module-echo-cancel` (WebRTC APM) в audio-plane (не в ассистенте); H даёт loopback-референс — **резолв D §10** | 2026-06-22 |
| Декодер | `symphonia` (Rust, MPL-2.0; AAC-патент на юр-проверку; Opus НЕ поддержан → `audiopus`); aptX/LDAC исключены (#12) | 2026-06-22 |
| BT-аудио | A2DP sink (SBC/AAC) + AVRCP; обрыв → cork+auto-resume; паринг/HFP — G/Connectivity (шов) | 2026-06-22 |
| Радио/стриминг | later (v3); FM требует тюнера (нет в hardware); стриминг — креды юзера, провайдер-агностик | 2026-06-22 |
| Lifecycle | boot аудио-плоскости Stage 1; стоп+mute-amp на ShutdownImminent; resume без авто-старта; не держит wake | 2026-06-22 |