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.
 
 
 
 
 
 

11 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 файлов
│   ├── 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 или 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"

Сборка

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

[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/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