Files
utun2/doc/ble_etcp.txt
T

584 lines
58 KiB
Plaintext
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.
BLE GATT → ETCP (chatgui desktop + Android)
Обновление31.10.2026: ник берётся из GATT NODE_NAME без BLE-сокращения; NODE_ID BE64
сверяется с UUID сервиса перед CCC/HELLO. Все платформы публикуют один GATT UUID,
не чередуют рекламу и не используют отдельный VC manufacturer publisher.
Штатные frontend автоматически читают профили соседей по одному запросу без DATA-сессии.
Полное имя хранится отдельно и имеет приоритет; обновление RSSI/UUID не стирает его.
Успешное чтение профиля не продлевает seen_us; повторы/round-robin — doc/ble_transport.txt.
Обновление31.10.2026: BLE задаёт single_path_rto_min_us=1000000 своему ETCP_LINK.
Пока он единственный согласованный UP-путь, первый DATA-повтор ждёт не менее1с,
последующие — не менее2с от предыдущей принятой отправки. Рассчитанный больший RTO
сохраняется. Отказы prepare/send не считаются принятыми отправками. При любом другом
согласованном UP-линке действует прежний RTO, даже если резерв временно занят.
Изменение UP-путей пересчитывает уже назначенный таймер от исходного sent_at_us.
При разных сроках выбирается ближайшая попытка, не обязательно голова FIFO.
ACK/keepalive/запросы окна используют прежние интервалы. Смена политики — etcp=info,
RTO с base/effective/first-or-retry — etcp=debug. Формат протокола не меняется.
Проверки: tests/test_etcp_tx_rto и общий BLE ETCP стенд.
Обновление30.10.2026: Android предпочитает PHY1M. Common BLE ограничивает число
целых DATA до ACK последнего фрагмента (config.tx_window); очередь tx_limit
и защитный предел12500байт учитываются дополнительно. Формат DATA/ACK прежний.
Выбор окна и актуальный контракт — doc/ble_transport.txt. Новые аппаратные
измерения выполнены отдельным стендом драйвера без ETCP.
Историческое состояние (29.10.2026):
Write Without Response/Notify, HIGH и PHY2M включены. BLE version3 добавляет
16-bit ID каждого DATA GATT-value, накопительный ACK по отдельной характеристике
не чаще15ms и окно12500байт на направление (фиксированные2Мбит/с ×50ms).
Локальный completion не возвращает кредит. Формат и таймауты — doc/ble_transport.txt.
Предыдущие исправления готовности BlueZ, старого ManufacturerData, incoming HIGH
и потолка DATA RTO сохранены. Ограничение очереди само по себе не считается
подтверждённым исправлением всей ETCP-нагрузки; аппаратные результаты ниже.
Общий код: tools/cgathui_common/ble_etcp.c/h.
Дубликаты одного ETCP_CONN сворачиваются после аутентифицированного UP победителя:
остаётся GATT с большим node_id central (исходящий для central, входящий для peripheral).
Порядок появления встречных линков не влияет на выбор. Одиночный линк с любым
направлением сохраняется. При одинаковом central только он выбирает ранее созданный
локальный handle; peripheral следует закрытию с его стороны, не сравнивает свои ID.
Закрывается только лишний ETCP_LINK штатным путём: неподтверждённые DATA возвращаются
в очередь, общий conn/его эпоха и прочие транспорты сохраняются. INFO discovery
содержит peer, keep/close BLE handles, ETCP path IDs, master node IDs и направления.
Платформенные backend остаются в lib/ble и android_ble.c; Qt не участвует в протоколе.
UTUN binary этот модуль не собирает. При активном UTUN-сервисе общего instance
BLE-драйвер не передаёт ETCP: CHAT и UTUN используют общий conn/normalizer,
поэтому разделить их уже упакованные пакеты внутри линка нельзя.
Обнаружение и объявления при этом остаются доступны.
Владелец в потоке uasync:
ble_etcp_create(ua, instance, &config)
ble_start(ble_etcp_radio(driver))
ble_advertise / ble_set_incoming — через заимствованный radio
ble_etcp_neighbors(driver, output, capacity) — копия свежих соседей
ble_etcp_connect(driver, node_id) — явное подключение к обнаруженному соседу
ble_etcp_destroy(driver) — обязательно до instance/uasync, вне callbacks
instance=NULL оставляет сырой BLE и discovery для аппаратных проб; штатные
frontend передают instance. config.callbacks — наблюдатели; received/writable
штатного ETCP принадлежат драйверу. Сырые TX/RX нельзя читать параллельно ETCP.
Таблица в управляющей структуре драйвера, не в instance и не в UI:
64 записи {node_id, short/full name, profile_read/at/retry, endpoint, RSSI, seen_us, broadcast_received}; срок свежести 30 секунд.
Каждое принятое валидное объявление увеличивает broadcast_received, включая одинаковые повторы.
Обновление имени, адреса и RSSI сохраняет счётчик. Он сбрасывается вместе с записью
при удалении по таймауту или замене другим соседом; новая запись начинает счёт с 1.
Desktop формирует видимый список из snapshot общего драйвера. Android получает
тот же C API через android_ble_driver(). node_id/name в эфире не аутентифицированы.
Android показывает счётчик под ником, desktop — в строке соседа как RX.
В discovery TRACE каждый пакет содержит peer_received; DEBUG удаления показывает итоговый received.
Автоматическое подключение проверяется раз в секунду и при появлении соседа:
- только работающий CHAT-сервис, полная разрешённая запись мембера source=0/deleted=0;
- нет живого BGP-пути в конкретной группе, включая маршрут через посредника;
- группа получает TOPO_PEER_REQUEST и владеет NCD уже в CONNECTING;
- GATT прикрепляется к тому же ETCP_CONN, которым владеет NCD;
- один GATT к обнаруженному node_id разделяется запросами нескольких групп;
- запрос снимается на READY, удалении группы/членства либо через 20 секунд;
готовую сессию продолжает держать сама группа;
- повтор GATT ограничен интервалом 5..6 секунд; появление объявления само по себе
не создаёт подключения к постороннему узлу.
Каждый GATT-канал предоставляет packet_link_driver: ctx/ua/name, bind/get_state,
send/set_rx_enabled/stop/destroy. Контракт — doc/packet_link.txt.
max_packet=1500 — размер непрозрачного пакета BLE, включая заголовки ETCP.
Более длинное пользовательское сообщение дробит нормализатор ETCP; получившиеся
пакеты BLE транспорт дополнительно фрагментирует под фактический ATT MTU.
TX limit=1 включает пакет в процессе фрагментации. У адаптера нет второй TX-очереди:
send сразу передаёт ll_entry в ble_tx_put. Готовность читается через ble_tx_ready;
после завершения целого пакета writable будит ETCP. При отказе владение не меняется.
Дополнительно ble_tx_ready учитывает ACK-кредит: после локальной отправки целого
пакета writable выдаётся, только если есть место в удалённом окне. При полном
окне ACK освобождает кредит и будит отправителя; INIT/keepalive проходят тот же лимит.
RX использует ll_queue общего транспорта и включается packet_link потребителем.
DATA передаётся через Write Without Response (client → server) и Notification
(server → client). BLE send завершает только локальную отправку; доставку и повторы
обеспечивает ETCP. INVITE остаётся отдельным Write Request/Indication каналом.
BLE ACK подтверждает приём GATT-value драйвером, не доставку целого ETCP-пакета.
После BLE HELLO/ACCEPT первым целым кадром каждой стороны идёт 40 байт:
'V' 'C' 'E' 'T' 01 00 00 00 | 32 байта X25519 public key
Драйвер сверяет производный node_id с HELLO и ключом уже существующего ETCP_CONN,
затем прикрепляет динамический линк. Это ещё не доказательство владения ключом:
его обеспечивает штатный зашифрованный INIT и handshake эпох ETCP. Следующие
кадры — неизменённые зашифрованные ETCP-пакеты, без IP-заголовков/добавочного framing.
До завершения обмена ключами действует дедлайн 20 секунд.
Допуск знакомого узла (30.10.2026)
--------------------------------
Политика хранится только в ETCP_LINK: etcp_link_add(conn, driver, require_known_node).
При флаге1 сам ETCP проверяет ключ после аутентификации пакета/STCP, до ответа INIT,
публикации UP и при подтверждении эпохи. Новый handshake проверяет заново; обычные DATA
используют положительный кэш линка. Протокол на проводе не меняется.
Проверка реализована в topo_groups_peer_key_known: активная своя CHAT-группа и полная
локальная запись source=0/deleted=0, точный X25519-ключ и производный node_id.
SQL принадлежит topo_node_sqlite_member_key_allowed. Объявление BLE, nodes-реестр или
пустая заглушка участника допуска не дают. Нет БД/групп или отказ чтения — нет допуска.
ETCP_CONN разделяет ключи/поток нескольких путей, но не хранит политику знакомого узла.
Штатные Android/desktop BT-драйверы создают BLE-линк с require_known_node=1, в том числе
на уже работающем общем соединении. Проверка предшествует UP этого линка, поэтому готовность
другого пути её не обходит. Сокетные TCP/UDP-пути UTUN имеют флаг0 без исключений и наследования.
Изолированные
аппаратные пробы без базы явно выбирают0; это не настройка штатного пользовательского UI.
Неизвестному bootstrap-ключу общий BLE-драйвер не создаёт ETCP_CONN. Это только
отрицательный preflight, не доказательство владения ключом. GATT остаётся максимум60с
в режиме invitation-only: отдельный канал INVITE доступен по прежней настройке приёма,
а DATA отбрасывается. Если приглашение привело к JOIN и ключ стал известным, драйвер
раз в секунду проверяет возможность повышения, затем запускает полноценный ETCP handshake.
Поэтому отказ неизвестному ETCP не ломает приглашения, но один HELLO/публичный ключ
никогда не публикует UP и не удостоверяет известного собеседника.
Диагностика линка в обеих GUI использует общий etcp_link_get_snapshot: подпись BLE,
физическое направление из ble_get_outgoing и фактические link_state/link_status
ETCP. GATT READY сам по себе не включает зелёный индикатор. Адреса IP, порты и имя
сетевого сокета для BLE отсутствуют. До прикрепления к ETCP попытка GATT не входит
в список ETCP-линков; протокольный is_server не подменяется направлением GATT.
BLE callbacks только обновляют состояние/будят отложенную обработку. Закрытие
GATT делается вне этих callbacks; packet_link откладывает destroy. При выключении
frontend закрывает свои ETCP-линки, отсоединяет ctx от manager и уничтожает радио;
поздний destroy ctx не обращается к уже уничтоженному manager. Ключи не логируются.
Категория диагностики discovery; для handshake/маршрутов — etcp, connection, bgp, ncd.
Проверки:
- tools/chatgui/build/test_ble_etcp: настоящий BLE common protocol + fake async GATT,
encrypted ETCP handshake, MTU23, 1500-byte duplex, потеря DATA после успешного
локального send и восстановление ETCP, TX busy/ownership/resume,
несовпадающий public key, teardown, реальные SQLite/NCD/BGP и READY двух групп.
- test_desktop_ble: UI policy и discovery через общий snapshot.
- lib/ble/tests/test_ble_transport.c: MTU23/247/517, пропущенные фрагменты,
bounded queues, независимые соединения и stop.
- Android: ./gradlew assembleDebug assembleDebugAndroidTest.
Ручная аппаратная проверка (один процесс VibeChat/probe на BLE-адаптер):
adb -s SERIAL shell am instrument -w -e etcp true -e seconds 180 \
com.utun.chat.test/com.utun.chat.BlePeerProbe
tools/chatgui/build/ble_etcp_probe 150
Обратное направление: instrumentation -e initiator true, desktop ... 150 listen.
ETCP-probe объявляет тестовое имя; desktop подключается только к нему.
Временно включается ETCP_ID_BLE_PROBE echo, выключенный в обычном приложении.
32 пакета по 1500 байт сверяются побайтно; успех — RESULT ... verified=32 failed=0.
Instrumentation восстанавливает Bluetooth-политику в finally. PHY2M и HIGH priority
запрашивает native backend; фактические параметры/результат запроса смотреть в логах.
Нагрузочная проба: ble_etcp_probe seconds [connect|listen] [data|invites] [packets] [window].
По умолчанию packets=32/window=1. Для заполнения канала: например,
tools/chatgui/build/ble_etcp_probe 90 listen data 4096 16
и Android с -e etcp true -e initiator true -e seconds 100.
Обратная роль: desktop connect и Android без initiator. В полёте не более window
запросов (1..64); каждый из packets (1..1000000) сверяется побайтно.
THROUGHPUT считает verified_bytes_each_direction и aggregate_bytes_per_second
за время от первого send до последнего проверенного echo, без GATT/ETCP handshake.
Это двунаправленный echo goodput, не скорость PHY. Неполный прогон имеет ненулевой exit.
При неполном прогоне THROUGHPUT complete=0 учитывает всё окно до остановки,
включая простой после последнего echo; RESULT failed=1 и ERROR содержат target/sent/verified.
До первого send разрыв/смена адреса возвращает пробу к discovery; разрыв во время
передачи завершает её с ошибкой. UTUN_TEST_DEBUG=1 включает ETCP trace/connection debug
на desktop для подробного разбора ACK/RTO без изменения штатных фильтров приложения.
Перед пробой выключить BLE обычного desktop GUI: два сервиса с одинаковыми UUID
на одном адаптере делают GATT discovery неоднозначным.
Проверено 2026-10-29:
- desktop CMake и Android arm64-v8a/x86_64 APK собираются;
- ./check.sh: 230/230 + proxy/burst/load; выбранные GUI/ETCP/BLE: 6/6;
- test_ble_etcp под ASAN/UBSAN с detect_leaks=1: без ошибок;
- Android (RZ8R72MG64B) инициирует GATT к Linux: 32×1500 байт echo совпали,
PHY2M, MTU517, фактический connection interval=12 (15 ms);
- Linux инициирует GATT к тому же Android: несколько ETCP-пакетов передаются,
затем HCI Connection Timeout (0x08). Сырой ble_peer_probe без ETCP также
воспроизводит HCI 0x08 (подтверждено btmon). Этот сбой нижнего BLE-стека
не устранён данным драйвером; обе аппаратные роли нельзя считать проверенными.
Приглашения через Bluetooth
---------------------------
Общий владелец регистрирует заимствованный chat_local_provider в instance;
сам список соседей остаётся в ble_etcp. chat_local объединяет LAN и BT по node_id.
При отправке выбирается доступный LAN endpoint, иначе BLE; при синхронном отказе
LAN допустим BLE. После принятой отправки транспорт сам выдаёт один конечный
статус: асинхронный таймаут не запускает рассылку по другому транспорту.
Явная отправка приглашения создаёт GATT при необходимости, без проверки членства
или BGP-пути. При активном UTUN приглашения доступны без добавления BLE в ETCP.
Характеристика f61a9023-4abc-45ac-98e1-761df4c13811: Write + Indicate.
Клиент пишет запросы/ответы в неё; сервер адресует indications одному клиенту.
DATA и INVITE имеют отдельные ll_queue и сборщики фрагментов, но одну незавершённую
GATT-операцию на соединение. Планировщик чередует фрагменты, HELLO имеет приоритет.
DATA TX использует tx_limit, INVITE TX допускает один целый пакет; RX ограничены
64 пакетами на канал. Необходимы подписки на обе характеристики.
После сборки INVITE: version(1)=1, kind(1)=1 запрос/2 ответ,
epoch(8 BE), request_id(8 BE), status(1), payload.
Эпоха случайная ненулевая на экземпляр драйвера. Запрос содержит готовую join-ссылку
1..1100 байт (тот же предел, что LAN), status=0. Ответ без payload: status=0 FAILED,
1 RECEIVED, 2 FORBIDDEN. RECEIVED означает доставку в обработчик приложения,
не вступление в группу. ATT completion сам по себе доставку приложению не подтверждает.
Ожидающих отправок до 8, дедлайн 30 с, до 3 попыток с интервалом 8 с.
Разрыв соединения/остановка завершают pending с FAILED. Повторы принятого запроса
подавляются 60 с по peer/epoch/request и SHA256 содержимого (кеш 64 записи).
Заполненный кеш отказывает новым запросам, не вытесняя ещё действующую защиту.
Отправитель не получает гарантии отсутствия доставки при FAILED/потере ACK.
[chatserver] bluetooth_invites_enabled=0 — отдельное разрешение, по умолчанию выключено.
Проверяется при каждом запросе, в том числе на существующем GATT. Не влияет на DATA,
LAN-приглашения или исходящую отправку. Новому входящему GATT также нужно разрешение
«Allow incoming Bluetooth connections». Desktop и Android показывают переключатель
во вкладке Bluetooth. JOIN выполняется только по подтверждению пользователя.
Содержимое приглашения и пароль не выводятся в логи (категория discovery).
Формат join-ссылки не изменён: для последующего JOIN по-прежнему нужны её IP-адреса.
Проверка: test_ble_transport (MTU23/247/517, независимые каналы), test_ble_bluez
(два клиента, адресные fd), test_ble_etcp (приложение/ACK, запрет, повторы,
потеря ACK, совместный ETCP, teardown и GATT по запросу без ETCP).
Физическая проба: ble_etcp_probe 60 listen invites; Android BlePeerProbe
с -e etcp true -e initiator true -e invites true -e seconds 50.
Проба использует синтетическую ссылку, не запускает JOIN и восстанавливает политику.
Проверено 2026-10-29 на Android RZ8R72MG64B и Linux/BlueZ:
- Android инициирует GATT: приглашение 939 байт туда/обратно, ACK обоих приложений;
параллельно проверены все 32 ETCP echo-пакета по 1500 байт.
- Desktop инициирует GATT: те же приглашения и ACK в обе стороны; за окно 45 с
успели завершиться 7/32 ETCP echo-пакетов, затем проба остановлена по лимиту времени.
Это не полный нагрузочный проход обратной роли.
- Windows backend проверен кросс-компиляцией MinGW/C++WinRT, без физического Win BT.
- Общие тесты: 230/230; отдельные BLE/BlueZ/UI и ASAN/UBSAN пройдены.
Write Without Response / Notification: проверка 2026-10-29
--------------------------------------------------------
Android, BlueZ и WinRT используют быстрый DATA, INVITE сохраняет Request/Indication.
Android HIGH/MTU517/2M сохранены; WinRT запрашивает ThroughputOptimized при наличии
API Windows 11. BlueZ D-Bus не предоставляет аналогичного per-link профиля.
Desktop GUI/Linux, Android APK+instrumentation (arm64/x86_64), WinRT cross-build PASS.
./check.sh: 230/230 + proxy/burst/load; BLE common/BlueZ/identity/ETCP/UI: 5/5;
BLE ASAN/UBSAN: 3/3; Android app/router unit tests: 770/770.
Телефон RZ8R72MG64B, один desktop BLE-сервис на адаптере:
- Android central: 512/512 ETCP echo по 1500 байт, window=16, по 768000 байт
в каждом направлении за 61.694 с; суммарный goodput 24897 B/s (12448 B/s
на направление). Приглашения 939 байт и ACK приложений прошли в обе стороны.
PHY2M TX/RX, MTU517; для Android central подтверждён interval=12 (15 ms).
- Сырой BLE, Android central: 96/96 echo, по 45636 байт за 4.473 с,
суммарно 20407 B/s. Размеры пакетов различаются, напрямую с ETCP не сравнивать.
- Desktop central: приглашения/ACK прошли, но за окно 85 с ETCP подтвердил
только 20/512 echo. Повтор без приглашений, window=1: 0/32 echo за окно 65 с,
после первого send 50.060 с повторялись INIT/INIT_RESPONSE и DATA RTO,
DATA_RTT_us=0; BLE-сборщик не регистрировал отбросов. Причина не установлена;
исправление этого сбоя не входит в подтверждённый результат профиля скорости.
Журналы текущего сеанса: /tmp/ble-speed-android-central-{desktop,logcat}.log,
/tmp/ble-speed-desktop-central-{desktop,logcat}.log,
/tmp/ble-speed-desktop-central-serial-retry-desktop.log и
/tmp/ble-speed-desktop-central-serial-live-logcat.log.
После аппаратных проб обычный desktop VibeChat перезапущен новой сборкой;
Android instrumentation восстановила обычную Bluetooth-политику в finally.
Диагностика очередей и аудит ошибок 2026-10-29
---------------------------------------------
Последующие пробы уточнили причину просадки desktop central. Локальный completion
WriteValue(type=command) означает приём фрагмента стеком, а не его передачу по радио.
tx_limit=1 ограничивает только нашу ll_queue; очередь BlueZ/ядра этим не ограничена.
Доказательства по журналам, без сравнения часов разных устройств:
- /tmp/ble-errors-final-hci.btsnoop и /tmp/ble-speed-errors-final-desktop.log:
сопоставлены 882 исходящих целых кадра по seq/размеру/FNV-отпечатку ciphertext.
От локального BLE completion до последнего ACL-фрагмента в HCI проходит
3.548..20.167 с; следовательно, очередь находится перед границей HCI TX.
Для 675 совпавших входящих кадров HCI RX → BLE callback занимает 0.6..32.9 мс,
обычно 1..2 мс. Часовой сдвиг форматов времени исключён по HCI RX/callback.
- ETCP в этой пробе повторяет DATA по RTO=1000000 us, пока исходные кадры
ещё находятся в нижней очереди. Повторные копии увеличивают задержку.
Начальный INIT также повторяется примерно через 50 мс и содержит padding.
- Результат контрольного запуска: 42/128 echo по 1500 байт, window=16,
63000 подтверждённых байт на направление за 65.730 с после первого send,
complete=0; общий лимит запуска 75 с. drops=0 и missing=0 на BLE-сборщике.
- Более ранний cause-load1: desktop локально завершил 233 кадра, Android получил
только первые 40; обратно завершено 262, получены первые 76. У полученных
кадров совпали размеры/отпечатки, внутренних разрывов нет — отсутствуют хвосты.
Дополнительная задержка относительно первого кадра выросла на 15.982/10.012 с.
- Сбой перемежающийся: отдельная проба desktop central window=1 прошла 32/32
за 11.148 с, суммарно 8612 B/s. Это не подтверждает устойчивость под нагрузкой.
В контрольной пробе Android также каждые ~6 с подключался к соседу
4A:2F:B0:6D:A6:35 со старыми свойствами RX=8 (Write), TX=32 (Indicate).
Сервис найден со status=0, но не поддерживает новые DATA-свойства. Причина отказа
теперь явно указывает отсутствующий Write Without Response/Notification.
Влияние этих параллельных подключений на скорость отдельно не измерено.
Локальный completion недостаточен для оценки фактического продвижения кадров.
До изменения wire-протокола необходимо проверять готовность GATT, профиль
соединения и поведение ETCP при RTT больше секунды. Последующее расследование
ниже подтвердило ошибки готовности/профиля и воспроизвело очередь без ETCP.
Произвольная задержка между send и смена на AcquireWrite сами по себе не дают
гарантии ограниченной очереди. Дополнительный credit-протокол пока не вводился.
Аудит диагностики:
- Android: ошибки SDK/callback сохраняют status, роль/адрес/link/channel;
отказы CCC сохраняют код API33, MTU — requested/reported/effective;
неожиданные закрытия/отклонённые чтения/записи имеют WARN, потерянный callback
или таймер Handler — ERROR, timeout server writer содержит его состояние.
- JNI: переполнение mailbox содержит generation/capacity/discarded;
поздние события удалённого link видны на TRACE. Тестовые уровни discovery/debug
восстанавливаются при detach и не остаются включёнными после instrumentation.
- BLE common: разрывы последовательности целых кадров учитываются отдельно
как missing; partial/drop не смешивается с ними. Некорректные фрагменты,
замена неполного кадра, отказ allocation и completion имеют контекст ошибки.
- BlueZ: ошибки recv/reply/socketpair сохраняют errno/текст; D-Bus ошибки
сохраняют имя и сообщение. WinRT: HRESULT и ATT_error/status доступны при
WARN, включая discovery/subscription, без необходимости включать DEBUG.
- ETCP: проверяются RNG salt/sc_encrypt и фактические результаты отправки
INIT/keepalive; busy — DEBUG, отказ — WARN/ERROR. Успешный лог называется
submitted, поскольку он не подтверждает доставку получателю.
Для сопоставления кадров и BLE ACK: debug=debug; для сводок очередей — discovery=debug.
Категория debug содержит DATA TX/RX с seq каждого GATT-пакета, ACK TX/RX с ID и кредитом,
а также wire TX/RX с отдельным seq, длиной и отпечатком целого кадра.
Для RTO/ACK ETCP — etcp=trace, connection=debug, keepalive=debug.
На desktop UTUN_TEST_DEBUG=1 включает trace диагностической категории;
Android BlePeerProbe включает её на время пробы. Ключи/ссылки приглашений не выводятся.
Проверки после аудита: C-ядро, desktop GUI/probe, Android APK/instrumentation
для arm64-v8a/x86_64 и WinRT cross-build собираются. BLE transport/identity/BlueZ,
четыре desktop packet-link/BLE теста и ETCP connect/packet_link/session проходят.
HCI можно снимать через sudo btmon -w FILE; обычному пользователю monitor запрещён.
Обычный desktop VibeChat после пробы восстановлен, политика Android восстановлена.
Причина длительного ожидания и исправление входящего HIGH
---------------------------------------------------------
Это очередь перед HCI TX, а не единая пауза ожидания PHY negotiation:
- /tmp/ble-ready-baseline-hci.log: ACL Connected success, MTU517, LL TX/RX=251,
PHY TX/RX=2M success до отправки ETCP INIT. Linux central выбрал interval=45ms,
latency=0 и supervision timeout=420ms. В этой пробе также был HCI timeout 0x08.
- В Android HIGH запрашивался только из исходящего BluetoothGatt callback.
Входящий GATT server запрашивал 2M, но не connection priority, поэтому Linux
central сохранял медленный профиль. PHY2M сам по себе interval не сокращает.
- /tmp/ble-ready-raw-{hci,desktop}.log: без ETCP и его повторов прошли все 96
raw echo; по 45636 байт на направление за 27.585s, суммарно 3309B/s.
Все 96 кадров сопоставлены с HCI по seq/длине/отпечатку. Последний исходящий
кадр попал в HCI через 26.035s после локального completion, входящие callback
следовали за HCI RX через 1..2.7ms. Следовательно, ETCP не является необходимым
условием возникновения очереди: стек принимает burst быстрее, чем обслуживает ACL.
Контроллер сообщил 6 LE TX-буферов по 251 байту; по Completed Packets они заняты.
- При изменении interval 45→7.5ms в отдельной пробе частота HCI TX увеличилась
примерно в шесть раз. После обновления параметры подтверждены событием
LE Connection Update Complete status=0; долговременного pending negotiation нет.
hcitool lecup имеет собственные особенности ожидания результата/длины event:
вывод команды и её exit недостаточны, согласование проверяется по HCI event.
- ETCP усиливает перегрузку: INIT вначале повторяется примерно каждые 50ms
с padding; DATA RTO ограничен 1s даже при уже измеренном KA RTT=5..6s.
В результате повтор может попасть в очередь раньше физической отправки оригинала.
На этапе исправления incoming HIGH алгоритмы INIT/RTO ещё не менялись;
последующее исправление RTO описано ниже.
Отдельно найден мешающий discovery: D-Bus sender :1.70, PID3047 kdeconnectd,
каждые 60s устанавливает Transport=bredr. BlueZ объединяет его с нашим Transport=le,
и контроллер выполняет Inquiry по 10.24s почти непрерывно. Штатное disable-backend
объявило BluetoothLinkProvider disabled, но не остановило старую discovery-сессию.
В изолированной пробе потребовался краткий restart KDE Connect с этим backend
выключенным; отсутствие Inquiry подтверждено HCI до восстановления backend.
Медленный drain/повторы сохранились и без Inquiry: это дополнительный фактор,
а не достаточное объяснение всех задержек. Backend KDE Connect затем возвращён
в enabled; его LAN backend и постоянные настройки Bluetooth не менялись.
Исправления:
- BlueZ не открывает кешированные UUID до Connected=1 и ServicesResolved=1.
После resolve повторно читается snapshot/MTU. Регрессионный private-bus тест
с доступным кешем и ServicesResolved=false падал до исправления, теперь проходит;
после сигнала resolve используется актуальный MTU517 вместо прежнего MTU23.
В аппаратной пробе видны оба snapshot: connected=1/resolved=0, затем resolved=1.
- GetManagedObjects используется только как metadata snapshot: закешированное
ManufacturerData больше не обновляет discovery/last_seen. Иначе старый RPA
телефона мог выдаваться за актуальный endpoint. Новые объявления поступают
только из live InterfacesAdded/PropertiesChanged. Регрессионный тест со старым
ManufacturerData в snapshot падал до исправления и проходит после него.
- Android после первой входящей CCC открывает BluetoothGatt handle к тому же ACL
исключительно для requestConnectionPriority(HIGH). У BluetoothGattServer нет
соответствующего публичного метода. Новый handle не выполняет discovery/DATA,
хранится у входящего Link и закрывается при его teardown. Ошибка настройки
логируется WARN и не отменяет работоспособный GATT server.
- /tmp/ble-ready-priority-fixed-{hci,logcat}.log: incoming HIGH accepted=true;
контроллер получил remote request 11.25..15ms, latency=0, timeout=5000ms,
Linux ответил success, Update Complete success установил interval=15ms.
PHY2M/MTU517 сохранены. Клиентский handle переиспользовал существующий ACL.
Ручной HCI настройки и отключения KDE Connect в этой пробе не было.
- В этой нагрузочной пробе сопоставлены 715 TX кадров: локальный completion→HCI
занял до 1.713s вместо ранее измеренных 20..26s. У 684 входящих кадров
HCI→callback занял 0.8..2.8ms. Очередь существенно уменьшилась, остаточные
задержки и недостаточный для такого RTT RTO требуют отдельной работы.
За окно 80s подтверждено 266/512 echo, complete=0, после первого send 70.876s,
суммарно 11259B/s. Это неполная нагрузочная проба, не успешные 512/512.
Дополнительные журналы: /tmp/ble-ready-isolated-*, /tmp/ble-ready-quiet-*,
/tmp/ble-ready-priority-check-*, /tmp/ble-ready-dbus.log.
Поздние повторения ETCP с HIGH всё ещё нестабильны: 115/128 и 27/128 за разные
окна; один профиль не устранил проблему ETCP целиком, все такие пробы complete=0.
Чистое радио после HIGH: /tmp/ble-ready-raw-noscan-fresh-* (несмотря на имя файла,
pauseScan в аргументах этого запуска НЕ было, scanning оставался включённым):
96/96, по 45636 байт за 4.021s, суммарно 22700B/s. У 96 совпавших исходящих
кадров очередь до HCI достигала 3.753s: большой burst всё ещё буферизуется.
/tmp/ble-ready-raw-noscan-valid-*: scanning Android остановлен с явным логом,
96/96 за 10.620s, суммарно 8594B/s. Остановка scanning не дала ускорения;
одиночная парная проба не доказывает отсутствие любого его влияния.
Обе пробы начинались после сброса оставшегося ACL предыдущего теста/GUI.
Первые две попытки сравнения прервались до передачи с le-connection-abort-by-local
и не учитываются. Raw probe теперь повторяет discovery после разрыва до первого
send; разрыв после начала передачи остаётся ошибкой.
Радио-проба умеет выбирать точный node_id, чтобы не подключаться к соседнему
устройству со старым протоколом:
ble_peer_probe connect 80 1657b3ea8281c0a8
UTUN_TEST_DEBUG=1 включает отпечатки кадров также в raw probe.
Проверки кода: C-ядро, desktop GUI/probes, Android APK/instrumentation arm64/x86_64
собираются; Android unit tasks, private-bus BlueZ и четыре desktop BLE/packet-link
теста проходят. Пробы восстанавливают GUI, Android-политику и временное состояние
KDE Connect. Сборку APK и запуск instrumentation нужно выполнять последовательно:
одна проба во время установки завершилась Process crashed и не учитывается.
Изоляция старого протокола и RTO при RTT больше секунды
------------------------------------------------------
Сосед 1588291579282de3 публиковал DATA RX=Write/TX=Indicate. Android пытался
подключиться к нему примерно каждые 6s и обнаруживал несовместимые свойства.
Новый wire-profile требует объявления VC/version=2; версия 1 отклоняется
на discovery, до connectGatt. JNI передаёт scan prefix из общего C codec,
Kotlin не хранит отдельную версию. Нужны новые сборки на обоих пирах.
В /tmp/ble-ready-priority-v2-* нет этих подключений: 128/128 echo за10.623s,
суммарно36149B/s, TX queue до0.554s. Длинные повторения остались нестабильны:
/tmp/ble-ready-desktop-load-v2-* 49/512, /tmp/ble-ready-android-central-v2-* 439/512.
В desktop-load-v2 при KA_RTT_us=2025400 повтор DATA назначался через1000000us;
далее наблюдался RTT2..6s с тем же потолком. Попытки удалялись раньше ACK,
повторы увеличивали очередь и мешали получить действующие DATA/BBR samples.
etcp_tx_rto_us теперь сохраняет ceil(1.2*max(DATA_RTT,KA_RTT)+1.5*jitter+3000us),
минимум10ms и начальное1s без RTT; ограничение сверху — представимость uint32.
Переполнение формулы считается в uint64 и логируется WARN при насыщении.
test_etcp_windows проходит все63 сценария, включая RTT2s/ACK1.5s без повтора,
таймер7.8672s и предельные значения. Формат DATA/ACK и модель BBR не менялись.
Аппаратная проба после исправления: /tmp/ble-ready-rto-desktop-v2-*.
MTU517, HIGH принят, PHY2M. За71.328s подтверждено183/512 echo, complete=0,
суммарно7697B/s; это НЕ успешный нагрузочный проход. Сопоставлены534 TX кадра:
local completion→HCI до4.583s; у503 RX кадров HCI→callback0.7..2.5ms.
До первого INIT_RESPONSE отправлены24 INIT (вначале каждые~50ms), после готовности
линия продолжает передавать кадры. Исправление RTO не ограничивает очередь ОС.
Начальный RTO без RTT всё ещё1s, и в этой пробе он сработал до первого измерения.
Нужен отдельный выбор ограничения стартовых INIT/нижней TX-очереди; изменение
одного таймера не считается подтверждённым устранением всех задержек BLE.
При полном check обнаружена некорректная фикстура test_etcp_server_lifecycle:
синтетический линк MTU=0 не мог отправлять keepalive, но тест ожидал роста счётчика.
После аудита ошибок счётчик увеличивается только при принятии пакета транспортом.
Фикстура теперь явно задаёт результат отправки через codec без сетевого I/O;
добавлена проверка, что busy/error сохраняют счётчик, а успешная отправка увеличивает.
Обратная роль после RTO: /tmp/ble-ready-rto-android-central-v2-repeat-*.
За76.272s подтверждено475/512 echo, complete=0, суммарно18683B/s. У1133 TX кадров
local completion→HCI до1.497s; у1111 RX кадров HCI→callback0.3..2.6ms.
Первая попытка /tmp/ble-ready-rto-android-central-v2-* получила Android GATT133
до открытия/передачи; она не является измерением скорости. Повтор выполнил обмен,
но также не закончил512 пакетов в заданном окне80s. Начальная настройка HIGH/PHY
асинхронна: запрос не подтверждает её завершения, фактический результат виден в HCI.
Длинная очередь остаётся и после успешного LE Connection Update Complete.
Итоговые проверки: ./check.sh230/230, proxy/burst/load PASS (178s);
test_etcp_windows63 сценария и исправленный server_lifecycle PASS. Desktop GUI/probe
и Android APK/instrumentation arm64/x86_64 пересобраны; APK установлен на A52.
Обычный desktop VibeChat и Android-политика после проб восстановлены.
BLE version3: ACK и ограничение очереди (29.10.2026)
---------------------------------------------------
Общий C-драйвер добавляет flow_id BE16 к каждому DATA GATT-value и отдельную
ACK-характеристику f61a9024-4abc-45ac-98e1-761df4c13811 (Write Request/Indication).
ACK содержит только последний полученный flow_id BE16. Public DATA/INVITE API
сохраняет владение ll_entry; backend имеет отдельный внутренний wire-канал ACK.
До opened обязательны все три подписки; ранний HELLO отвергается с WARN.
Все DATA, включая INIT/keepalive/bootstrap, расходуют одно окно12500байт на направление.
Кредит возвращает только удалённый ACK; ring хранит размеры, не payload.
При полном окне TX останавливается, ACK продолжает работать; подтверждение будит writable.
Без прогресса ACK15s действует самостоятельный watchdog, не зависящий от native pending.
ACK объединяется до последнего ID, следующий разрешён через15ms после completion
предыдущего. Это также гарантирует >=15ms между отправками и даёт DATA/INVITE время
между ACK даже при медленном ATT. Первоначальный безусловный приоритет ACK давал
голодание DATA: /tmp/ble-ready-ack16-desktop-v3-* получил0/512 за233.032s,
хотя окно строго соблюдалось, HCI TX queue была <=0.753s. Raw-проба до исправления
планировщика /tmp/ble-ready-raw-ack16-v3-* дала64/96, complete=0.
Новая точная регрессия20ms ACK completion + непрерывный RX падала до исправления;
после него встречный DATA приходит примерно за21ms. Изменения ETCP для этого не нужны.
Проверки: общий transport (MTU23/247/517, дуплексное окно, ACK>=15ms,70000 IDs,
повторы/неверный ACK, ранний HELLO, настоящий15s watchdog), identity, private-bus BlueZ,
ETCP/packet-link/UI проходят. ASAN/UBSan с detect_leaks=1:3/3 PASS.
./check.sh230/230 + proxy/burst/load PASS (180s). Android APK/instrumentation
arm64-v8a/x86_64, app/router unit tasks и desktop GUI/probes собираются;
WinRT backend с ACK проходит MinGW/C++WinRT cross-build, физического Win radio нет.
Аппаратная проверка после исправления планировщика:
- /tmp/ble-ready-ack16-fair-desktop-v3-*: Linux central, Android A52 peripheral.
512/512 echo, по768000байт в каждую сторону за72.344s, суммарно21232B/s.
Максимум DATA flight:8713байт desktop,4442 Android, оба меньше12500.
Минимальный интервал отправок ACK:27.958ms desktop,31.561ms Android.
С HCI сопоставлены1220 TX/1057 RX кадров по seq/размеру/отпечатку:
local completion→HCI TX <=0.174s, HCI RX→callback0.6..2.9ms.
Это успешный полный проход, не прежняя незавершённая проба на183/512.
- /tmp/ble-ready-ack16-fair-android-central-v3-*: Android central, Linux peripheral.
512/512 echo, по768000байт в каждую сторону за115.141s, суммарно13340B/s.
Максимум DATA flight:10204байта desktop,4411 Android, оба меньше12500.
Минимальный интервал отправок ACK:59.451ms desktop,44.817ms Android.
С HCI сопоставлены1148 TX/899 RX кадров: local completion→HCI TX <=0.358s,
HCI RX→callback0.4..1.7ms. В обоих запусках подтверждены MTU517/PHY2M/15ms.
Скорость указана суммарно для echo; это не полезная скорость2Мбит/с.
После обеих проб обычный desktop GUI перезапущен новой сборкой, Android-политика
восстановлена instrumentation finally. Настройки KDE Connect не менялись.
BLE version4: ACK Without Response / Notify (30.10.2026)
--------------------------------------------------------
В обычной сессии version3 ACK id=328 занял общий pending на118ms: Android
06:23:32.041850 ACK TX, .152142 DATA_queue=1 pending_ms=110 ack_pending=1,
flight844/12500, следующий DATA seq=316 только в .160182. Write Request/Indication
блокировал DATA при свободном окне. Это исправлено во всех backend:
DATA/ACK используют Without Response/Notify; INVITE сохраняет Request/Indication.
Новые свойства ACK требуют версии4 на обеих сторонах; старые объявления отвергаются.
Свежие ID объединяются при интервале>=15ms после локального completion. Последний
ACK повторяется через100ms даже без новых DATA, одним отменяемым таймером на link.
Свежий DATA сокращает ожидание повтора до ближайшего разрешённого15ms срока.
ACK не подтверждает ACK, не расходует кредит; старый ACK не продлевает15s watchdog.
Категория debug: ACK TX содержит repeat, ACK sent — ID и elapsed_us операции.
Регрессия: полные окна в обоих направлениях, три ACK подряд в каждом потеряны,
локальные completions успешны. Прежний код не завершал обмен за5s; новый восстановил
его примерно за0.495s без прикладной повторной отправки. Сохраняются проверки12500
байт, ACK>=15ms,70000 ID через wrap, ошибочного ACK, teardown и настоящего15s timeout.
Общие transport/BlueZ/identity:3/3 PASS; ETCP/desktop/packet-link/driver:5/5 PASS.
ASAN+UBSan с leak detection:3/3 PASS. Desktop, Android APK/instrumentation/unit tasks,
WinRT MinGW/C++WinRT собраны; физический Windows по-прежнему не проверен.
Для точного потока32kbit/s добавлены bytes/interval_ms в ble_etcp_probe:
ble_etcp_probe 45 listen data 800 16 80 20
Передача планируется по абсолютным срокам, чтобы шаг poll не снижал заданную скорость.
Вывод LATENCY содержит RTT avg/max и максимальный промежуток между echo.
Аппаратные результаты A52/BlueZ, с MTU517, PHY2M и интервалом15ms:
- /tmp/ble-ack4-listen-32k-exact-*: Android инициирует GATT,800/800 echo,
по64000байт за16.047s,7977B/s суммарно. RTT avg87.1ms/max195.2ms, RX gap max91.5ms.
ACK completion Linux median40us/max576us, Android median3671us/max19089us;
минимальный интервал ACK15045us/17568us, flight остаётся<=12500байт.
- /tmp/ble-ack4-connect-32k-ab-*: Linux инициирует GATT,800/800 echo,
по64000байт за16.081s,7959B/s суммарно. RTT avg89.1ms/max230.1ms, RX gap max75.7ms.
Во время этой пробы Bluetooth backend KDE Connect временно выключался и восстановлен.
Inquiry продолжался: этот запуск не доказывает причинную связь с KDE Connect.
HCI подтверждает ACK Write Command/Notification вместо Request/Indication.
Ограничения аппаратных результатов: /tmp/ble-ack4-connect-32k-clean-* завершил786/800
за37.920s. ACK completion был<=2.8ms, но local completion->HCI TX достиг1.436s,
то есть остаётся отдельная задержка ниже драйвера при уже исправленном ACK.
В HCI есть повторный Classic Inquiry; его причинная роль пока не доказана.
Неудачная /tmp/ble-ack4-connect-32k-exact-* вообще не начала DATA: оставшийся ACL
предыдущей пробы и новый RPA дали HCI ACL Connection Already Exists(0x0b).
Это отделено от результатов передачи; перед чистой пробой закрывался только её ACL.
Полное устранение лагов в обычной сессии этими echo-проверками не подтверждено.