# 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 файлов │ ├── utun_config_api.h/c # конфиг-провайдер (Kotlin → C) │ └── invite_link_c.h/c # invite-ссылки (encode/decode, совместимы с десктопом) │ ├── 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 # план реализации по этапам ``` ## Технологии | Слой | Язык | Фреймворк | |------|------|-----------| | 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](https://developer.android.com/studio#command-line-tools-only) или 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/`: ```properties sdk.dir=/home/user/Android/Sdk ``` Путь `ndk.dir` можно не указывать — Gradle скачает NDK сам (версия из `ndkVersion` в `app/build.gradle.kts`). Если NDK уже установлен отдельно: ```properties ndk.dir=/home/user/Android/Sdk/ndk/29.0.14206865 ``` ### Установка Android SDK (без Android Studio) ```bash 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" ``` ## Сборка ### APK (Android) ```bash cd tools/chatgui-android export ANDROID_HOME=/home/user/Android/Sdk ./gradlew assembleDebug # APK: app/build/outputs/apk/debug/app-debug.apk ``` ### Headless (Linux, для отладки сетевого стека) ```bash cd tools/chatgui-android && mkdir -p build && cd build cmake .. && make -j4 ./headless/headless ``` ## Прошивка на телефон ```bash # Проверить доступные устройства adb devices # Установить APK adb -s 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)`. Формат (генерируется автоматически, пользователь не редактирует): ```ini [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: [allowed_keys] allow_all=yes [ntp] enabled=no [chat] storage_autoload=1 storage_autoload_maxsize_mb=10 storage_maxsize_gb=1 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= 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): ```bash tools/logreceiver/logreceiver_restart.sh ``` Скрипт ротирует логи (история 10 запусков: `.log` → `.log.1` → … → `.log.10`), убивает старый демон и запускает новый на `0.0.0.0:9999`, вывод в `logreceiver_output.log`. При каждом запуске логи фиксируются в истории и начинается свежий лог. В конфиге Android: ```ini log_udp_ip = log_udp_port = 9999 ``` ## Ключевые файлы для доработок ### C-слой - `libutun_lite/invite_link_c.h/c` — invite-ссылки (encode/decode, бинарный формат идентичен десктопу `tools/chatgui/src/invite_link.cpp`) - `jni_bridge/android_jni_bridge.h/c` — bridge API: `utun_bridge_join_channel()`, `utun_bridge_send_message()`, JNI-функции - `libutun_lite/utun_config_api.h/c` — конфиг-провайдер (Kotlin → C через коллбэки) - `libutun_lite/utun_sources.cmake` — список всех .c файлов, компилируемых в libutun_lite ### Kotlin-слой - `data/InviteLink.kt` — парсинг `utun://base64blob`, `deriveNodeId()` (SHA256 от pubkey), `serializeAddrs()` - `data/NativeLib.kt` — JNI-обёртка, все `external fun native*` - `ui/screens/JoinChannelDialog.kt` — диалог: ввод/вставка ссылки, debounce-декодинг, preview, Connect - `ui/screens/QrScanScreen.kt` — CameraX + ML Kit сканер, фильтрует `utun://` ссылки - `ui/screens/ChannelListScreen.kt` — список каналов, кнопка Join (QrCode), кнопка Create - `MainActivity.kt` — навигация экранов, QrScan → JoinDialog flow ### Headless - `headless/headless_control.c` — управляющий TCP-сокет, команды: `join`, `connect`, `subscribe`, `debug_level` - `headless/headless_main.c` — точка входа, парсинг аргументов, uasync event loop ## 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`