You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

15 KiB

AGENTS.md — chatgui-android (Android P2P Chat)

Android-версия чатгуи: P2P чат на STCP (TCP), UI на Jetpack Compose (Kotlin). C-ядро: libutun_lite (выборочная компиляция нужных .c из lib/ и src/).

Весь UDP-стек (ETCP, BBR, loadbalancer, NAT, routing, TUN) исключён. Транспорт: только STCP (X25519 + AES-CCM поверх TCP).

Состав проекта (5 частей)

tools/chatgui-android/
├── libutun_lite/           # C-ядро (статическая библиотека)
│   ├── CMakeLists.txt
│   ├── utun_sources.cmake   # список компилируемых .c файлов
│   ├── instance_lite.h/c     # жизненный цикл (старт/стоп/рестарт, ключи)
│   ├── utun_config_api.h/c   # конфиг-провайдер (Kotlin → C)
│   ├── invite_link_c.h/c     # invite-ссылки (encode/decode, совместимы с десктопом)
│   ├── voice_recorder.h/c    # запись голосовых (PCM→Opus→канал)
│   └── attachment_sender.h/c # отправка файлов в канал
│
├── headless/                # CLI для Linux (тестирование без Android)
│   ├── CMakeLists.txt
│   ├── headless_main.c       # точка входа, uasync event loop
│   └── headless_control.c/h  # управляющий TCP-сокет (JSON/text протокол)
│
├── jni_bridge/              # JNI прослойка C ↔ Kotlin
│   └── android_jni_bridge.c/h  # C API + JNI-функции (компилятся только для Android)
│
├── app/                     # Android приложение (Gradle + NDK + Compose)
│   ├── build.gradle.kts
│   └── src/main/
│       ├── AndroidManifest.xml
│       ├── cpp/              # NDK: jni_bridge + libutun_lite → libutun.so
│       │   └── CMakeLists.txt
│       ├── java/com/utun/chat/
│       │   ├── ChatApplication.kt     # Application, инициализация
│       │   ├── MainActivity.kt        # Compose UI, навигация экранов
│       │   ├── data/
│       │   │   ├── NativeLib.kt       # JNI-обёртка
│       │   │   ├── ChatRepository.kt  # репозиторий (JNI + JSON)
│       │   │   ├── InviteLink.kt      # парсер utun://base64blob
│       │   │   ├── ConfigProvider.kt  # конфиг из DataStore
│       │   │   ├── LogManager.kt      # лог-менеджер
│       │   │   └── ServerEntry.kt     # модели данных
│       │   ├── viewmodel/
│       │   │   └── ChatViewModel.kt   # StateFlow для UI
│       │   ├── ui/screens/
│       │   │   ├── ChannelListScreen.kt  # список каналов + join/create
│       │   │   ├── ChatScreen.kt         # сообщения + ввод
│       │   │   ├── JoinChannelDialog.kt  # диалог подключения по invite
│       │   │   ├── QrScanScreen.kt       # CameraX + ML Kit QR-сканер
│       │   │   ├── SettingsScreen.kt     # настройки
│       │   │   └── LogScreen.kt          # лог-просмотр
│       │   ├── ui/components/
│       │   │   └── MessageBubble.kt      # бабблы сообщений + InputBar
│       │   └── headless/
│       │       └── HeadlessService.kt    # фоновый сервис
│       └── res/
│
└── doc/                     # документация
    ├── AGENTS.md            # этот файл
    ├── ARCHITECTURE.md      # подробная архитектура
    └── IMPL_PLAN.md         # план реализации по этапам

Chat-подсистема (src/chat/)

Все модули из src/chat/ компилируются в libutun_lite через file(GLOB_RECURSE). Описания каждого модуля — см. корневой AGENTS.md, секция «Chat (src/chat/)».

Технологии

Слой Язык Фреймворк
C-ядро C99 OpenSSL, SQLite3
Bridge C + JNI Android NDK 29
Android UI Kotlin Jetpack Compose, Material3, CameraX, ML Kit
Сборка C CMake 3.16+ GCC (Linux) / Clang (Android NDK)
Сборка APK Gradle 8.x AGP 8.x, Kotlin 1.9

Зависимости

Для сборки APK (Android)

Компонент Версия Установка
JDK 17+ apt install openjdk-17-jdk
CMake 3.22+ apt install cmake
Android SDK API 36 через sdkmanager или Android Studio
Android NDK 29.0.14206865 sdkmanager "ndk;29.0.14206865"
Gradle 8.5 (wrapper) ./gradlew скачает сам
Kotlin 1.9.20 встроен в Gradle плагин
adb platform-tools apt install adb (для прошивки)

OpenSSL для APK предсобран под arm64-v8a и x86_64, лежит в app/src/main/cpp/openssl/. Собирать отдельно не нужно.

Для Headless (Linux, отладка без телефона)

Компонент Установка
CMake 3.16+ apt install cmake
GCC / Clang apt install build-essential
OpenSSL dev apt install libssl-dev
pthread встроен в libc

Настройка окружения

Создать local.properties в корне tools/chatgui-android/:

sdk.dir=/home/user/Android/Sdk

Путь ndk.dir можно не указывать — Gradle скачает NDK сам (версия из ndkVersion в app/build.gradle.kts). Если NDK уже установлен отдельно:

ndk.dir=/home/user/Android/Sdk/ndk/29.0.14206865

Установка Android SDK (без Android Studio)

cd /tmp && wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
unzip commandlinetools-linux-*.zip -d ~/Android
mkdir -p ~/Android/Sdk/cmdline-tools
mv ~/Android/cmdline-tools ~/Android/Sdk/cmdline-tools/latest

export ANDROID_HOME=~/Android/Sdk
export PATH=$ANDROID_HOME/cmdline-tools/latest/bin:$PATH

yes | sdkmanager --licenses
sdkmanager "platforms;android-36" "build-tools;36.0.0" "ndk;29.0.14206865"

Сборка

./build.sh              # clean + сборка + прошивка на подключённый телефон
./build.sh noinstall     # только сборка, без прошивки
./build.sh <SN>          # clean + сборка + прошивка на конкретный девайс

APK (Android)

cd tools/chatgui-android
export ANDROID_HOME=/home/user/Android/Sdk
./gradlew assembleDebug
# APK: app/build/outputs/apk/debug/app-debug.apk

Headless (Linux, для отладки сетевого стека)

cd tools/chatgui-android && mkdir -p build && cd build
cmake .. && make -j4
./headless/headless

Прошивка на телефон

# Проверить доступные устройства
adb devices

# Установить APK
adb -s <serial> install -r app/build/outputs/apk/debug/app-debug.apk

# Смотреть логи
adb logcat -s utun:*

# Control-сокет (при запущенном HeadlessService):
adb forward tcp:9999 tcp:9999
nc localhost 9999
> {"id":1,"cmd":"status"}
> {"id":2,"cmd":"join","link":"utun://..."}

Конфиг

Конфиг хранится в Android DataStore (ConfigProvider.kt) и передаётся в C-ядро при старте как INI-текст через nativeStart(configText).

Формат (генерируется автоматически, пользователь не редактирует):

[global]
my_node_name=MyDevice
my_public_key=<64 hex chars X25519>
my_private_key=<64 hex chars X25519>
db_path=/data/data/com.utun.chat/files
db_sync_enabled=1

[server:main]
addr=0.0.0.0:<listen_port>

[allowed_keys]
allow_all=yes

[ntp]
enabled=no

[chatserver]
storage_autoload=1
storage_unit_size=10M
storage_total_size=1G
opus_codec_preset=1
compressor_enabled=0
compressor_max_gain_db=25
compressor_rise_rate=10
media_download_max_peers=3

[debug]
console_level=info

# Опционально — UDP-лог
[log_udp]
ip=<ip хоста>
port=9999

Ключи (my_public_key, my_private_key) генерируются автоматически при первом запуске и сохраняются в keys.conf в db_path. Имя узла (my_node_name) берётся из настроек DataStore или Build.MODEL.

Логирование через logreceiver (только Android)

Debug-сообщения с телефона дублируются через UDP на приёмник tools/logreceiver/.

Запуск (всегда через скрипт, до старта utun):

tools/logreceiver/logreceiver_restart.sh

Скрипт ротирует логи (история 10 запусков: .log → .log.1 → … → .log.10), убивает старый демон и запускает новый на 0.0.0.0:9999, вывод в logreceiver_output.log. При каждом запуске логи фиксируются в истории и начинается свежий лог.

В конфиге Android:

log_udp_ip = <ip хоста>
log_udp_port = 9999

Ключевые файлы для доработок

C-слой

  • libutun_lite/instance_lite.h/c — Жизненный цикл uTun для Android: запуск/остановка C-ядра в отдельном потоке, перезапуск при смене конфига, генерация и обновление X25519-ключей, health-check
  • jni_bridge/android_jni_bridge.h/c — JNI-прослойка Kotlin↔C: все операции из UI (отправка сообщений, вход в каналы, голосовые, статус, настройки) и обратные вызовы (логи, события). Здесь же — JNI-функции, компилируемые только для Android
  • libutun_lite/invite_link_c.h/c — Кодирование и декодирование invite-ссылок utun:// в бинарный формат. Совместим с десктопной версией
  • libutun_lite/utun_config_api.h/c — Поставщик конфигурации из Kotlin в C-ядро через callback-интерфейс (get_string, get_int64, get_int)
  • libutun_lite/voice_recorder.h/c — Запись голосовых сообщений: накопление PCM-сэмплов с компрессором, кодирование в Opus-файл, отправка в канал через chat_core
  • libutun_lite/attachment_sender.h/c — Отправка файлов в канал: копирование в media-директорию и регистрация через chat_core (media_index → db_sync)
  • libutun_lite/utun_sources.cmake — Список всех .c файлов, компилируемых в libutun_lite. Новые файлы добавлять сюда

Kotlin-слой

  • data/NativeLib.kt — Kotlin-обёртка над C-библиотекой: все вызовы из Kotlin транслируются в JNI-функции
  • data/ChatRepository.kt — Хранилище данных: буферизация сообщений и каналов между C-ядром и UI через StateFlow
  • data/InviteLink.kt — Разбор invite-ссылок: извлекает ID канала, публичный ключ, адреса для подключения
  • data/ConfigProvider.kt — Поставщик конфигурации из Android DataStore в C-ядро через callback-интерфейс
  • data/LogManager.kt — Сбор и хранение логов из C-ядра через log-callback
  • viewmodel/ChatViewModel.kt — ViewModel: StateFlow-состояние для UI (список каналов, сообщения, статус)
  • ui/screens/ChannelListScreen.kt — Главный экран: список каналов, кнопки Join (по invite-ссылке) и Create
  • ui/screens/ChatScreen.kt — Экран чата: список сообщений + поле ввода
  • ui/screens/JoinChannelDialog.kt — Диалог подключения к каналу: ввод/вставка invite-ссылки, предпросмотр, кнопка Connect
  • ui/screens/QrScanScreen.kt — QR-сканер для invite-ссылок (CameraX + ML Kit)
  • MainActivity.kt — Точка входа Android-приложения: навигация между экранами, QrScan → JoinDialog flow

Headless (тестирование без телефона)

  • headless/headless_main.c — Точка входа headless-режима: парсинг аргументов, запуск C-ядра в uasync event loop
  • headless/headless_control.c/h — Управляющий TCP-сокет: JSON-команды (status, send, join, subscribe) и асинхронные события

Invite-ссылки (механика подключения к каналу)

Формат идентичен десктопной версии:

utun:// + base64(
    version(1B, 0x01) |
    channel_id(8B BE) |
    [header(1B: bits0-1=count-1, bits2-5=family_flags) | pubkey(32B) |
     addrs*(socketId(1B) | address(4B v4/16B v6) | port(2B BE))]+
)

Node ID вычисляется как SHA256(pubkey)[0:8] & 0x7FFFFFFFFFFFFFFF (совместимо с sc_derive_node_id_from_pubkey()).

Правила разработки

  • Все C-файлы: C99, стиль как в корневом AGENTS.md (4 пробела, snake_case, DEBUG_* макросы)
  • Все новые C-файлы добавлять в libutun_lite/utun_sources.cmake
  • Все новые Kotlin-файлы — Compose, Material3, coroutines/StateFlow
  • ViewModel не держит ссылки на Context
  • Логи в C — через debug_set_log_hook → bridge → Kotlin LogManager
  • Конфиг в C — через utun_config_provider_t коллбэки в Kotlin
  • Перед коммитом: головная C-сборка + ./gradlew assembleDebug