From fb4e5851521794f6e4f158bc9a8ec643dcc76fea Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 22 Jun 2026 19:08:52 +0300 Subject: [PATCH] =?UTF-8?q?docs(domain=20G):=20v2=20=E2=80=94=20=D1=81?= =?UTF-8?q?=D0=B2=D1=8F=D0=B7=D1=8C/=D1=82=D0=B5=D0=BB=D0=B5=D1=84=D0=BE?= =?UTF-8?q?=D0=BD=20(Connectivity+Phone),=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5?= =?UTF-8?q?=20adversarial-=D1=80=D0=B5=D0=B2=D1=8C=D1=8E=20(15=20=D0=BD?= =?UTF-8?q?=D0=B0=D1=85=D0=BE=D0=B4=D0=BE=D0=BA)=20+=20=D0=BA=D1=80=D0=BE?= =?UTF-8?q?=D1=81=D1=81-=D0=B4=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Новый домен G: Connectivity-core (WiFi/LTE/tethering + BT-адаптер/паринг) + Phone-ап (HFP-звонки/контакты). Многоагентный adversarial-ревью: 24 находки, 15 подтверждено (default-refute; tech-измерение — 0, техника устояла), все применены. Ключевое из ревью: - BT-шов резолвнут двунаправленно: владелец BlueZ — Connectivity-core (G §3 ↔ H §6/§15 ✅). - Граница голоса: только HFP спаренного телефона; встроенный модем — data-only (VoLTE/eCall вне скоупа). - SIM PIN/PUK-флоу (sim_locked/no_sim) + captive-portal детект (portal/limited, не ложный online) — модем v1. - Connectivity поднимается на Stage 2 (синхр. с architecture §6). - Мультипаринг: ровно один active-телефон для HFP/PBAP (Dial/CallStateChanged однозначны). - SCO-loss mid-call → CallState=audio_lost + снятие роли phone_call → H раскорчивает медиа (не залипает). - Входящий-оверлей не перекрывает реверс-вид (z-order overlay-слота → C); tethering-петля AP+tether запрещена. - PBAP-синк фоновый; отказ-пути паринга/no_service симметрично J/H. Кросс-док: H §6/§15 (BlueZ ✅) + якоря D §147/§148→§10; architecture §3 (Connectivity+BT) /§6 (Stage 2); ipc §3 (Type+tether, State enum, CallState=audio_lost); security-privacy §7 (контакты/журнал/SMS local-first); hardware §4 (Bluetooth); tech-stack (NM/MM/BlueZ); C §11 (z-order overlay). Co-Authored-By: Claude Opus 4.8 --- docs/architecture.md | 4 +- docs/contracts/hardware.md | 2 +- docs/contracts/ipc.md | 4 +- docs/contracts/security-privacy.md | 1 + docs/domains/c-shell-ux.md | 1 + docs/domains/g-connectivity-phone.md | 229 +++++++++++++++++++++++++++ docs/domains/h-media-audio.md | 15 +- docs/tech-stack.md | 1 + 8 files changed, 245 insertions(+), 12 deletions(-) create mode 100644 docs/domains/g-connectivity-phone.md diff --git a/docs/architecture.md b/docs/architecture.md index 842123f..80849b1 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -63,7 +63,7 @@ data-plane — см. §4. | **Settings/State** | центральная конфигурация и состояние, пишет в `/data` | общий источник правды; единственный писатель в `/data`-конфиг | | **Security/Perm-Broker** | выдаёт/режет capabilities по манифесту | привратник всех привилегий | | **Plugin/App-Host** | запуск, супервизия, песочница апов и плагинов | посредник доступа к шине; владеет sandbox-механизмом | -| **Connectivity** | обёртка ModemManager/NetworkManager, статус сети | управляет сетевым железом | +| **Connectivity** | обёртка ModemManager/NetworkManager **+ BlueZ-адаптер/паринг**, статус сети | управляет сетевым и BT-железом (общий радиоресурс) | ### First-party апы (отдельные процессы на SDK, расширенные права) @@ -158,7 +158,7 @@ graph TD **Быстрый boot фазами (цель — < 10 c до интерактива):** - **Stage 0 (мгновенно):** загрузчик → splash. Низколатентный путь задней камеры/парктроника, если включена задняя. - **Stage 1 (~3–5 c):** ядро-минимум (шина + Power + Settings + **Perm-Broker + App-Host**) → **Shell с первым кадром** (home-тайлы кликабельны). Broker/App-Host нужны раньше Shell: Shell — тоже ап и поднимается/цепляется к шине через них. -- **Stage 2 (фоном):** Vehicle-Data, Assistant, Media, Nav прогреваются после того, как UI интерактивен. +- **Stage 2 (фоном):** Vehicle-Data, Assistant, Media, Nav, **Connectivity** прогреваются после того, как UI интерактивен. **Завершение:** ACC-off → Power/Lifecycle инициирует graceful shutdown (systemd-таргет): апам сигнал «сохранись», Settings флашится в `/data`, размонтирование, снятие питания через supercap/MCU-копилот. Вместе с read-only rootfs это защита от corruption. diff --git a/docs/contracts/hardware.md b/docs/contracts/hardware.md index 6647c5e..48d3dfb 100644 --- a/docs/contracts/hardware.md +++ b/docs/contracts/hardware.md @@ -82,7 +82,7 @@ fail-safe при отказе/зависании самого MCU** (незав | **Дисплей** | HDMI + USB-тачскрин | MIPI-DSI / LVDS панель | | **CAN/OBD** | ELM327 (USB/BT) | нативный CAN-трансивер → **SocketCAN** | | **GPS** | USB/UART, NMEA | **внешняя/активная антенна** (LNA); 1PPS — опц. (PPS-точность времени; на USB-донглах обычно нет) | -| **Связь** | USB-модем (ModemManager) / Wi-Fi | — | +| **Связь** | USB-модем (ModemManager) / Wi-Fi / **Bluetooth** (BlueZ: HFP/A2DP/PBAP) | — | | **Аудио** | I2S codec + усилитель (+ mute/enable-GPIO — anti-pop) | — | | **Микрофон** | USB **mic-массив** (wake-word, шумоподавление) | — | | **Радио** *(later)* | — | FM-тюнер (Si47xx-класс) — нужен для радио (домен H §7); 🟡 добавить или отказаться | diff --git a/docs/contracts/ipc.md b/docs/contracts/ipc.md index 101bdbc..b17d025 100644 --- a/docs/contracts/ipc.md +++ b/docs/contracts/ipc.md @@ -86,8 +86,8 @@ ### `ru.shturman.Connectivity` — сеть (обёртка NM/MM) - **Методы:** `GetStatus() → status`, `ListNetworks() → [...]`, `Connect(id)`, `Disconnect()`. -- **Сигналы:** `ConnectivityChanged(online)`, `NetworkChanged(...)`. -- **Properties:** `Online` (bool — «сервис ещё не поднят» ≠ «offline»), `Type` (wifi/modem), `SignalStrength`. +- **Сигналы:** `ConnectivityChanged(state)`, `NetworkChanged(...)`. +- **Properties:** `Online` (bool — «сервис ещё не поднят» ≠ «offline»), `Type` (wifi/modem/tether), `State` (offline/portal/limited/online + модем sim_locked/no_sim/no_service — домен G), `SignalStrength`. ### `ru.shturman.Location` — GPS/положение (владелец — домен K) - **Properties:** `Latitude`, `Longitude`, `Heading`, `Speed`, `FixQuality` (enum `no_fix`/`fix_2d`/`fix_3d`/`augmented`/`dead_reckoning`), `HDOP`, `satellites`, `ts`; per-property quality для `Speed`/`Heading` (зануление у нуля — domain K §2). diff --git a/docs/contracts/security-privacy.md b/docs/contracts/security-privacy.md index 4e60b3c..3dd2b38 100644 --- a/docs/contracts/security-privacy.md +++ b/docs/contracts/security-privacy.md @@ -133,6 +133,7 @@ PipeWire-capture/location). Для `audio_in`/`location` — **while-in-use**: | Текст запроса | локально → онлайн-LLM при необходимости | только текст, только в онлайн-режиме | | Данные машины (OBD/DTC) | локально | **в промпт онлайн-LLM** — только онлайн, по запросу **И при явном (отзываемом) согласии** на весь исходящий промпт; офлайн — не уходит | | Память о водителе (`.md`) | локально | нет (без явного согласия) | +| Контакты/журнал/SMS (PBAP/MAP, домен G) | локально (fscrypt) | нет (без явного согласия) | | Телеметрия | локально | **opt-in**, по умолчанию выключена | - Онлайн-LLM — только **RU-провайдеры** (GigaChat/YandexGPT), данные в РФ (152-ФЗ). diff --git a/docs/domains/c-shell-ux.md b/docs/domains/c-shell-ux.md index 167365d..e462903 100644 --- a/docs/domains/c-shell-ux.md +++ b/docs/domains/c-shell-ux.md @@ -123,6 +123,7 @@ Shell** (его пробрасывает bubblewrap при `ui_*`, security-priv - ◻️ **Мультидисплей, профили** — later. - ✅ **Контракт Location/GPS** — в [k-sensors §2](k-sensors-peripherals.md) (QoS, FixQuality, zero-clamp скорости у нуля; C потребляет занулённую `Speed`). Арбитраж GPS-vs-OBD (v2) — открыт. - ◻️ **Какие руль-действия гейтятся в distraction** — парный с K §10. +- ◻️ **Z-order overlay-слота** (входящий-вызов G §6 vs реверс-вид J §6 vs ассистент/уведомления) — правило «вид манёвра > уведомление». → §4 + G/J. - 🟡 **Slot-протокол поверхностей** (`ru.shturman.shell_slot`: surface→слот, slot-token, peer-creds) — спроектировать (§4). --- diff --git a/docs/domains/g-connectivity-phone.md b/docs/domains/g-connectivity-phone.md new file mode 100644 index 0000000..a638ba2 --- /dev/null +++ b/docs/domains/g-connectivity-phone.md @@ -0,0 +1,229 @@ +# Домен G — Связь / телефон + +> Сетевой слой (WiFi/LTE-модем/tethering) **и** телефония (BT-паринг, HFP-звонки, контакты, +> проекция). Две сущности: **Connectivity** — привилегированный core-сервис (владеет сетевым/BT- +> железом); **Phone** — first-party ап на SDK (звонки/контакты). Транспорт для онлайн-ассистента +> (D), OTA/телеметрии (L) и стриминга (H). + +Статус: **v2 (на ревью).** v2 — после adversarial-ревью (15 находок). +Связано с: [architecture.md](../architecture.md) (§3 Connectivity/Phone, §5 карта связей, §6 boot-Stage) · [ipc.md](../contracts/ipc.md) (`Connectivity`, `Phone`) · [security-privacy.md](../contracts/security-privacy.md) (§3 `network`, §5 runtime-грант, §7 приватность/152-ФЗ) · [hardware.md](../contracts/hardware.md) (§4 модем/WiFi/BT/mic/amp, §3 load-shed) · [tech-stack.md](../tech-stack.md) (NM/MM/BlueZ) · домены **H** (§3 `phone_call`, §6 BT-аудио-шов), **D** (§3 online-LLM, §5 креды, §6/§10 интенты/AEC), **C** (§4 слоты, §7 distraction, §9 ввод), **K** (§3 руль-кнопки), **B** (§4 load-shed, §7 sleep/wake), **L** (облако/компаньон), **I** (нав/TMC) · [principles.md](../principles.md) (#1,#2,#3,#6,#7,#8,#11,#12,#13) + +--- + +## 1. Назначение и границы + +- **Connectivity (core):** IP-связность для companion-слоя — WiFi-клиент, LTE-модем, tethering от + телефона; приоритет/failover путей, статус сети; владелец сетевого и **BT-адаптера** (§3). +- **Phone (first-party ап):** BT-паринг UX, **HFP-звонки** (hands-free), контакты/журнал (PBAP), + опц. сообщения (MAP); звук звонка — роль `phone_call` в арбитре H (§4). +- **Границы (красные линии):** + - **Не safety-critical (#1):** **eCall / экстренный вызов — вне скоупа** (регулируемая safety-система). + - **Голос — только HFP спаренного телефона; встроенный LTE-модем — data-only** (CS-voice / VoLTE — вне + скоупа: отдельный крупный стек; усиливает границу eCall — головблок физически не голосовой узел сети). + - **Модем — НЕ шлюз к машине (#2):** Connectivity даёт IP только companion-слою; **никакого моста + модем↔CAN**, никакого удалённого управления узлами. Удалённый доступ к данным машины (если будет, через + L) — **read-only + opt-in**, не через G. + - **Проекция (CarPlay/Android Auto)** — тяжёлые лицензионные ограничения, не first-party, later (§8). +- **Plane:** control/состояние — D-Bus (`Connectivity`/`Phone`); **звук звонка — PipeWire** (роль + `phone_call`, политика — H §3); сам IP-трафик — вне D-Bus. + +## 2. Connectivity (core-сервис) + +- **Пути данных (провайдер-агностично #8):** (1) **WiFi-клиент** (домашняя/известная сеть на стоянке); + (2) **LTE-модем** (USB, SIM/APN — ModemManager); (3) **tethering** от телефона (BT-PAN / WiFi-hotspot телефона). +- **Приоритет/failover:** политика выбора активного пути (напр. WiFi > tethering > metered-LTE), бесшовное + переключение; **metered-aware** (крупные загрузки — OTA L/A — не тянуть по дорогой LTE без согласия). +- **Состояние связности — НЕ бинарно на уровне источника правды:** NM connectivity-check различает + `portal`/`limited` (ассоциация + IP есть, реального интернета нет — публичный WiFi за captive-portal) от + настоящего `online`. **Портал не рапортуется как online** — иначе D (online-LLM) / L (OTA) ломаются на + ложном «online». Прохождение портала — UX вне MVP, но детект обязателен (инвариант «не маскировать»). +- **SIM/модем-стейты:** PIN-locked SIM на boot/вставке → состояние **`sim_locked`** (и `no_sim`/`no_service`), + отличное от `offline` (как «сервис не поднят» ≠ offline); запрос **PIN — через слот Shell** (C §4, как + входящий-оверлей §6), **PUK**-fallback после исчерпания попыток (ModemManager `Sim.SendPin`/`SendPuk`). +- **Обёртка NetworkManager/ModemManager** (hardware §4); статус/состояния — `ru.shturman.Connectivity` (§11). +- **Boot-Stage:** Connectivity поднимается на **Stage 2** (фоном, как Vehicle-Data/Assistant; architecture §6), + не в Stage 1 → статус-бар C §3 до первого ответа = «unknown»; online-LLM (D §3, v1) до прихода Connectivity + деградирует по #3 (offline-first), не виснет. +- **Offline-first (#3):** сеть в движении нестабильна → потребители (D, H, L) **деградируют, не виснут**; + Connectivity лишь честно сообщает состояние, не маскирует. Безопасность: модем — отдельный процесс (net-ns). +- **Фаза:** WiFi-клиент + LTE-данные (+ SIM-unlock) — **v1** (нужно online-ассистенту D §3); hotspot/AP — later (§7). + +## 3. Bluetooth (core: адаптер + паринг) *(резолв BT-шва H §6; синхронизировано в H §6/§15)* + +- **Владелец BlueZ-адаптера и паринга — Connectivity-core** (общий аппаратный радиоресурс → один привилегированный + владелец, как сеть). 🟡 может выделиться в отдельный BT-сервис, если разрастётся (§15). +- **Паринг:** BlueZ-Agent (PIN/just-works) — механизм в core; **UX выбора устройства/подтверждения — в + Phone-апе/Shell** (слот C §4). Список спаренных, доверие, реконнект — здесь. **Сбой паринга** (неверный + PIN / timeout / reject устройства) → видимая неблокирующая индикация + откат к списку устройств, без зависания. +- **Активное устройство (мультипаринг):** одновременно активен **ровно один телефон для телефонии (HFP/PBAP)**; + при нескольких спаренных — выбор active-устройства (last-connected / явный выбор в Phone-апе, слот C). A2DP-медиа + может жить на **другом** устройстве (координация H §6). Это делает `Dial`/`CallStateChanged` (§11) однозначными. +- **Диспетчеризация профилей** к потребителям спаренного устройства: + - **HFP** (звонок) → **Phone-ап (G)**, §4; + - **A2DP/AVRCP** (медиа) → **Media-ап (H §6)**; + - **PBAP/MAP** (контакты/сообщения) → **Phone-ап (G)**, §5. +- **HFP-аудио** идёт через PipeWire (bluez5-модуль, mSBC/CVSD) — H маршрутизирует по роли `phone_call`. +- **Фаза:** v2 (вместе с Phone и A2DP H). + +## 4. Телефония — HFP-звонки (Phone-ап) + +- **Состояния вызова:** `idle`/`incoming`/`dialing`/`active`/`held`(опц.)/**`audio_lost`** — публикуются на + шине (§11), чтобы H проактивно корчил медиа (H §3 crash-safe: сигнал `Phone` → cork ДО появления аудио-ноды). +- **Аудио:** downlink (голос собеседника) → усилитель; uplink — **салонный mic-массив** (hardware §4), + тот же, что у ассистента → во время звонка mic отдан HFP-аплинку (политика арбитража H §3), ассистент + подавлен. AEC/денойз на uplink — желателен (тот же тракт, что H §4 / D §10). +- **Звонок — это телефона, не наш:** головблок — hands-free-периферия. +- **Потеря HFP-аудио-линка в середине звонка** (SCO рвётся — телефон вышел из зоны, — но вызов жив на AG): + **отдельное событие от hangup.** Phone эмитит `CallStateChanged → audio_lost` (§11), **синхронно снимая + заявку роли `phone_call`** → H гарантированно опускает проактивный cork (медиа не залипает; H §3), mic + освобождается, подавление ассистента снято. **Инвариант:** проактивный cork снят ⇔ оверлей не показывает + hands-free-разговор (одно событие на обе оси). UI (§6) → «звонок продолжается на телефоне». +- **Фаза:** v2. + +## 5. Контакты / журнал / сообщения (PBAP/MAP) + приватность + +- **PBAP:** синхронизация контактов и журнала вызовов с телефона (имя вместо номера, набор по имени §6). + **Синхронизация — фоновая/ленивая (#11)**, не блокирует UI и не задерживает доступность HFP; на общем + BT-линке **голос (HFP) приоритетнее** фоновой PBAP-синхронизации. **MAP** (SMS) — later (чтение вслух §6). +- **Приватность (#7, 152-ФЗ — высокочувствительно):** контакты, журнал, тексты SMS — **персональные данные**. + **On-device, local-first**; **никогда** в облако/онлайн-LLM без явного согласия (как память водителя D §7). + Доступ — audit-log (security-privacy §7 / a-base §9); at-rest — fscrypt-поддерево (a-base §3). Чистка — + factory-reset (a-base §12) + при отвязке устройства. +- **Фаза:** контакты v2; SMS/MAP v3. + +## 6. Управление вызовом и distraction (#6) + +- **Ответ/сброс — аппаратно, не голосом:** кнопки **мультируля** (answer/hangup) и тач (C §9, K §3, + uinput→Wayland-input) — крупная single-tap-аффорданс, доступна и на скорости (distraction блокирует + *сложное*, не одно-действие; C §7). **Во время звонка mic занят HFP** → голосового «положи трубку» нет + (явное решение, не пробел). +- **Набор — голосом (до звонка):** «позвони <контакт>» — **assistant-интент** (Phone — first-party, + регистрирует `assistant_intents`; роутинг/коллизии D §6 + plugin-sdk §5), резолв имени по PBAP active-устройства (§3/§5). +- **Входящий вызов — оверлей** в слоте Shell (C §4), упрощённый на скорости (distraction — владелец C §7); + `phone_call` прерывает ассистента (лестница H §3). **При активном реверс/парктроник-виде (J §6) входящий- + оверлей НЕ перекрывает live-вид камеры** — деградирует до компактной формы (полоса/PiP), сохраняя + answer/decline; z-order overlay-слота — у C (§15 → C). +- **Имя не синхронизировано** по PBAP (реконнект/большая книга §5) → оверлей показывает **номер** (#3 graceful), + имя подтянется позже. +- **Фаза:** v2. + +## 7. WiFi-hotspot / tethering *(later)* + +- **Головблок как AP** (раздать **текущий активный uplink** §2 — не обязательно LTE) и/или приём tethering + от телефона (§2). NM-управление. +- **Инвариант радио:** AP-раздача и приём WiFi-tether делят **одно WiFi-радио** → взаимоисключающие, если + железо не поддерживает concurrent STA+AP (зависимость от hardware §4); конфигурация «AP-uplink = тот же + телефон, что и tether-источник» — **запрещена (петля)**. +- Энергобюджет/нагрев (hotspot тянет) — с B/hardware. **Фаза:** v3. + +## 8. Проекция — CarPlay / Android Auto *(later, лицензионно-ограничено)* + +- 🟡 **Серьёзные ограничения (#12, юр.):** **CarPlay** требует Apple MFi-чип + лицензию (NDA, несовместимо с + open-source); **Android Auto**-проекция — проприетарный протокол Google; открытые реализации + (OpenAuto-класс) — реверс-инжиниринг, юридически серо, часто GPL. → **не first-party**; реалистично — + community-плагин под ответственность интегратора **или** вне скоупа. +- Если появится: аудио/видео проекции — роли `projection`/`phone_call` в арбитре H (§3); поверхность — слот C. +- **Фаза:** v4/неопределённо; решение — отдельно (§15). + +## 9. Lifecycle (шов с B) + +- **Load-shedding:** при power-loss модем/WiFi — среди сбрасываемых нагрузок (hardware §3, B §4) → Connectivity + отрабатывает пропажу транспорта как обычный offline (#3), потребители деградируют (in-flight HTTP D — таймаут + D §5; teardown через `ShutdownImminent`→`StopApp` оркестрирует B §4). +- **Sleep/wake (B §7):** модем/радио выключены в `sleep`/`battery_cutoff` (бюджет АКБ #5); опц. wake по сети — later. +- **Звонок при ACC-off:** головблок — лишь hands-free; на shutdown HFP-аудио прекращается, **звонок продолжается + на телефоне** (как при потере SCO §4) — активный звонок shutdown не блокирует. +- **Реконнект:** на boot/`accessory` Connectivity переподнимает известные WiFi/спаренные BT (resume медиа H §9 + зависит от этого). + +## 10. Приватность и безопасность (#7, #1, #2, #12) + +- **Local-first ПДн:** контакты/журнал/SMS — §5; никаких автоматических выгрузок. +- **Модем ≠ шлюз к CAN (#2):** изоляция companion-IP от шины машины — нет пути модем→Vehicle-Data→CAN-write + (его не существует). Удалённое — только read-only/opt-in через L. +- **eCall/экстренный — вне скоупа (#1).** +- **Лицензии (#12):** **BlueZ / ModemManager / NetworkManager** (и obexd для PBAP/MAP) — **GPL-2.0**; используем + как **отдельные системные демоны через D-Bus** (не линкуем в наши Rust-бинари) → copyleft изолирован + процессом (#12-допустимо). Наш код (`zbus`-обёртки) — MIT. + +## 11. IPC / интерфейсы + +- **`ru.shturman.Connectivity`** (ipc §3, ядро): `GetStatus()`/`ListNetworks()`/`Connect(id)`/`Disconnect()`; + сигналы `ConnectivityChanged(state)`/`NetworkChanged(...)`; properties `Online`(bool), **`State`** + (`offline`/`portal`/`limited`/`online` + модем `sim_locked`/`no_sim`/`no_service`), `Type` + (`wifi`/`modem`/**`tether`**), `SignalStrength`. Расширение enum `Type` (`tether`) + `State` + BT-устройства/ + выбор active — **расширить ipc §3 (хвост)**. +- **`ru.shturman.Phone`** (ipc §4, домен G): `Dial(number|contact)`/`Answer()`/`Hangup()` (адресуют active- + устройство §3); сигнал **`CallStateChanged(state, peer)`** (`idle`/`incoming`/`dialing`/`active`/`held`/ + **`audio_lost`** — SCO потерян, вызов на телефоне §4) — на него H проактивно корчит/раскорчивает медиа + (H §3); property `CallState`, `Contacts`(read). Закрывает ipc §4-делегацию H/I/**G**. +- **Гейтинг:** `network` (security-privacy §3/§5, runtime-грант); HFP-аудио — PipeWire-нода роли `phone_call` + (H §3); контакты стороннему — **не отдаём** без явного грана (приватность §5/§10). +- **UI:** входящий-оверлей/набор/PIN-запрос — слот Shell (C §4); статус сети — статус-бар (C §3). + +## 12. Dev-симулятор (#13) + +- **Сеть/модем — управляемые состояния** (уже в dev-environment: online/offline/слабый сигнал/**portal**/ + **sim_locked**, для offline-first тестов D/H/L). Добавить: **фейковый BT-стек** (паринг + **сбой паринга**, + профили HFP/A2DP/PBAP, мульти-устройство), **сценарии вызова** (incoming/active/hangup/**SCO-loss** → проверка + cork/un-cork медиа H §3), **мок-контакты** (PBAP без реального телефона). → dev-environment. + +## 13. Функции + +| функция | MVP/later | зависит от | фаза | +|---------|-----------|------------|------| +| Connectivity-сервис (WiFi-клиент + LTE-данные + статус + SIM-unlock) | **MVP** | hardware §4, NM/MM | v1 | +| Состояние связности (portal/limited детект, не ложный online) | **MVP** | NM connectivity-check | v1 | +| Приоритет/failover путей + metered-aware | **MVP** | — | v1 | +| BT-адаптер + паринг (core) + диспетчер профилей + active-устройство | **MVP** | hardware (BT), BlueZ | v2 | +| HFP-звонки (состояния + аудио `phone_call` + SCO-loss) | **MVP** | H §3, mic/amp | v2 | +| Отказ-пути (сбой паринга, no_sim/no_service) | **MVP** | — | v2 | +| Контакты/журнал (PBAP, фоновый синк) + privacy | **MVP** | security-privacy, a-base §3 | v2 | +| Управление вызовом (руль/тач) + входящий-оверлей (vs реверс) | **MVP** | C §4/§7/§9, K §3 | v2 | +| Голосовой набор «позвони X» (assistant-интент) | later | D §6 | v2 | +| tethering от телефона (BT-PAN / hotspot) | later | — | v2/v3 | +| WiFi-hotspot (AP, раздача uplink) | later | hardware/B | v3 | +| SMS/сообщения (MAP) + чтение вслух | later | D (TTS) | v3 | +| Проекция (CarPlay/Android Auto) | later | лицензии (§8) | v4/неопр. | + +## 14. Зависимости + +- **Вниз:** hardware (§4 модем/WiFi/**BT-адаптер**/mic/amp, §3 load-shed), NM/MM/BlueZ/obexd (tech-stack), PipeWire + (HFP-аудио через H), a-base (§3 fscrypt контактов, §9 audit, §12 reset). +- **Вбок:** **H** (роль `phone_call`, BT-аудио A2DP/AVRCP — H цепляет к нашему адаптеру; arbiter ducking), + **D** (online-LLM поверх Connectivity; интенты «позвони»/«прочитай SMS»; AEC-тракт §10), **C** (слоты входящего/ + набора/PIN, distraction, мультируль-ввод, z-order overlay), **K** (руль-кнопки answer/hangup), **B** (load-shed, + sleep/wake), **L** (Connectivity — транспорт OTA/телеметрии/компаньона), **I** (RDS-TMC — задел H §7). +- **Вверх:** статус сети/звонка для Shell; `network` — потребляют апы/плагины (гейтинг security-privacy). + +## 15. Открытые вопросы + +- 🟡 **BT-адаптер в hardware §4:** добавить Bluetooth в периферию (резолвится этим коммитом). → hardware. +- 🟡 **Владелец BlueZ:** Connectivity-core (рек., §3) vs выделенный BT-сервис — подтвердить. +- 🟡 **Телефонный стек HFP:** PipeWire-bluez5 (аудио) + BlueZ-D-Bus (управление) — достаточно, или нужен + **oFono** (richer call control: held/swap, call-waiting, 3-way, DTMF — тоже GPL-демон)? → реализация/tech-stack. +- 🟡 **Проекция (CarPlay/Android Auto):** не-first-party / плагин / вне скоупа — решить (лицензии §8). → отдельно. +- ◻️ **Z-order overlay-слота** (входящий-вызов vs реверс-вид vs ассистент vs уведомления) — общее правило + «вид манёвра > уведомление». → C (§4 слот-модель) + J §6. +- ◻️ **Политика приоритета путей** (WiFi/tether/LTE, metered-пороги, failover-гистерезис, пороги SIM-ретраев) — числа. → реализация. +- ◻️ **Контакты/журнал/SMS в security-privacy §7** (потоки данных) — добавить строку local-first (резолвится этим коммитом). → security-privacy. +- ◻️ **152-ФЗ для контактов/звонков** (оператор ПДн, основание) — legal-трек (как BYO-LLM, security-privacy §7). +- ◻️ **Wake-on-network / входящий в sleep** — нужен ли, бюджет АКБ. → B. +- ◻️ **tethering vs встроенный модем** — оба или приоритет одного (стоимость SIM vs зависимость от телефона). → later. + +--- + +## Журнал решений (домен G) + +| Решение | Выбор | Дата | +|---------|-------|------| +| Структура | Connectivity (core, сетевое+BT-железо, Stage 2) + Phone (first-party ап); оба в домене G | 2026-06-22 | +| Пути данных | провайдер-агностично: WiFi/LTE/tethering, приоритет+failover, metered-aware; связность не бинарна (portal/limited) | 2026-06-22 | +| Голос | **только HFP спаренного телефона; встроенный модем — data-only** (VoLTE/CS вне скоупа); eCall вне скоупа (#1) | 2026-06-22 | +| BlueZ | владелец — Connectivity-core; active-устройство = ровно один телефон для HFP/PBAP; профили HFP→Phone, A2DP/AVRCP→H, PBAP/MAP→Phone (**резолв H §6**) | 2026-06-22 | +| Звонок | HFP hands-free; звук — роль `phone_call`; SCO-loss → `audio_lost` + снятие роли (раскорчивает H); звонок «телефона» | 2026-06-22 | +| Управление | ответ/сброс — руль/тач single-tap (mic занят HFP → нет голос-hangup); набор — голосом (assistant-интент) | 2026-06-22 | +| Приватность | контакты/журнал/SMS — local-first, fscrypt, не в облако без согласия (152-ФЗ); PBAP-синк фоновый | 2026-06-22 | +| Красные линии | модем ≠ шлюз к CAN (#2); GPL-демоны (BlueZ/NM/MM/obexd) изолированы процессом (#12) | 2026-06-22 | +| Проекция | CarPlay/AA — лицензионно тяжело → не-first-party/плагин/вне скоупа, v4-неопр. | 2026-06-22 | +| Фазы | Connectivity-данные+SIM v1; BT/HFP/контакты v2; hotspot/SMS v3; проекция v4 | 2026-06-22 | diff --git a/docs/domains/h-media-audio.md b/docs/domains/h-media-audio.md index fa825a7..551b86f 100644 --- a/docs/domains/h-media-audio.md +++ b/docs/domains/h-media-audio.md @@ -78,7 +78,7 @@ capture-нода не «дакает» вывод; дакает медиа им intra-role, состояние для UI) — компонент Media-апа, подписан на `Assistant.StateChanged`/`Phone`/`Nav`/ `Power` (ipc §3–4). До Media-апа (v1) хватает статической политики + ролей ассистента. -## 4. AEC и loopback-референс *(резолв D §147)* +## 4. AEC и loopback-референс *(резолв D §10)* - H **публикует monitor/loopback-tap смешанного выхода** как референс для эхоподавления (без него wake-word во время воспроизведения и barge-in невозможны). @@ -86,8 +86,8 @@ capture-нода не «дакает» вывод; дакает медиа им пути capture микрофона; референс — monitor-порты выхода. **Не внутри ассистента** (D потребляет уже очищенную capture-ноду). *(`module-echo-cancel` — отдельный модуль PipeWire, не `module-filter-chain`: последний — иной механизм (граф LADSPA/LV2/builtin), пригоден лишь для опц. кастомного денойза.)* - → закрывает «место AEC (в audio-plane vs внутри ассистента)» из D §147 в пользу audio-plane. -- Beamforming/денойз салона (D §148) — отдельно (mic-массив, hardware); AEC ≠ денойз. + → закрывает «место AEC (в audio-plane vs внутри ассистента)» из D §10 в пользу audio-plane. +- Beamforming/денойз салона (D §10) — отдельно (mic-массив, hardware); AEC ≠ денойз. ## 5. Локальное аудио @@ -113,7 +113,8 @@ capture-нода не «дакает» вывод; дакает медиа им **cork/пауза транспорта + auto-resume по реконнекту**, не крэш, видимая индикация. - **Шов с G/Connectivity:** BT-адаптер, паринг и профиль **HFP** (звонок) — **не H** (домен G/Connectivity). H цепляет A2DP/AVRCP к уже спаренному устройству. Сосуществование A2DP↔HFP на одном устройстве — через - политику (§3): входящий `phone_call` корчит A2DP-`media`. Владелец BlueZ — ◻️ (§15, с G). + политику (§3): входящий `phone_call` корчит A2DP-`media`. Владелец BlueZ-адаптера/паринга — + Connectivity-core (домен G, резолв G §3); сосуществование A2DP↔HFP — политика §3. - **Проекция (CarPlay/Android Auto)** несёт свои медиа+звонок — её аудио идёт ролями `projection`/`phone_call`; контроль/протокол проекции — **G**. H лишь маршрутизирует звук по политике. @@ -223,10 +224,10 @@ capture-нода не «дакает» вывод; дакает медиа им - 🟡 **Декодер/кодеки:** `symphonia` (рек.) + **AAC-патент** (роялти) — юр-проверка; Opus → `audiopus`; SBC/AAC по BT, **aptX/LDAC исключены** (#12). → tech-stack. - 🟡 **FM-тюнер:** добавить в hardware §4 периферию **или** отказаться от радио. → hardware. -- 🟡 **Владелец BlueZ/паринга** (Connectivity vs G vs shared) + сосуществование A2DP↔HFP. → G/Connectivity. +- ✅ **Владелец 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 §147; синхронизировано в d-assistant §10). + loopback-tap — §4. → резолв §4 (синхронизировано в d-assistant §10). - ◻️ **Ранний звук на Stage-0-реверсе** (park-beep до поднятия аудио-плоскости) — нужен ли отдельный путь. → J/E/B. - ◻️ **Split политики:** сколько в статическом WirePlumber vs Rust-координаторе (+ crash-safety проактивного cork, тайминги гистерезиса/фейда дакинга). → реализация. @@ -247,7 +248,7 @@ capture-нода не «дакает» вывод; дакает медиа им | 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 §147** | 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 | diff --git a/docs/tech-stack.md b/docs/tech-stack.md index 5cfce17..22efd42 100644 --- a/docs/tech-stack.md +++ b/docs/tech-stack.md @@ -43,6 +43,7 @@ | **OS base** | Armbian/Debian (RK3588), ядро ближе к mainline | read-only rootfs + overlay | | **Init / lifecycle** | **systemd** | ядро; апы/плагины — App-Host | | **CAN/OBD (read-only)** | **SocketCAN** (`socketcan`) + **ISO-TP** (`can-isotp`/`CAN_ISOTP`) для нативного OBD | ELM327 (мульти-протокол, прячет ISO-TP) на старте; `python-OBD` — в симуляторе | +| **Связь / BT** | NetworkManager · ModemManager · BlueZ (+ obexd; опц. oFono) | GPL-демоны через D-Bus (изолированы, #12); HFP-аудио — PipeWire bluez5 | | **Wake-word** | **openWakeWord** | RU-фраза «Штурман»; Porcupine отвергнут (проприетарный, #12) | | **VAD** | Silero VAD | через ONNX Runtime | | **STT** | Vosk · Silero | офлайн, RU; через биндинги / `ort` |