Kotlin + Jetpack Compose VPN proxy client (Shadowsocks AEAD SIP004, SOCKS5 RFC1928/1929, HTTP/HTTPS CONNECT) with per-app whitelist via VpnService and the vendored hev-socks5-tunnel engine (JNI). Includes the post-review fixes to the connection lifecycle, reconnect teardown, journal, app-filter auto-reconnect, and the tab/timeline layout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 KiB
5 KiB
vojo proxy
Нативный Android-клиент для прокси: Shadowsocks, SOCKS5, HTTP, HTTPS. Per-app whitelist, минимальный расход батареи, маленький размер (release APK ≈ 2.4 МБ). Дизайн — в стиле мессенджера vojo (Dawn/Fleet: тёмные поверхности, hairline-границы, один фиолетовый акцент, monospace для технических данных).
Стек
- Kotlin + Jetpack Compose + Material3, Coroutines, DataStore, kotlinx.serialization.
- VpnService для создания tun-интерфейса и per-app whitelist (
addAllowedApplication). - tun2socks: нативный движок
hev-socks5-tunnel(C, event-loop, ~320 КБ) — самый экономичный по CPU/батарее способ конвертировать IP-пакеты из tun в потоки. - minSdk 28, targetSdk 35, только
arm64-v8a(Samsung S23).
Архитектура потока данных
Выбранные приложения
│ (per-app whitelist через VpnService.addAllowedApplication)
▼
tun-интерфейс ──fd──► hev-socks5-tunnel (нативный, JNI)
│ говорит SOCKS5 на 127.0.0.1:<port>
▼
LocalSocks5Server (Kotlin, loopback)
│ мост на нужный апстрим
┌─────────────────────┼─────────────────────┬───────────────┐
▼ ▼ ▼ ▼
Shadowsocks SOCKS5 HTTP CONNECT HTTPS CONNECT
(AEAD SIP004) (RFC 1928/1929) (RFC 7231) (TLS + CONNECT)
- TCP проксируется для всех 4 протоколов. Цель приходит уже как IP/домен в SOCKS5-форме.
- UDP/DNS:
hevотдаёт UDP через SOCKS5 UDP ASSOCIATE. Для SS/SOCKS5 — полноценный UDP-релей; для HTTP/HTTPS (UDP не поддерживается протоколом) — DNS-over-TCP fallback (порт 53). - Все апстрим-сокеты защищены
VpnService.protect(), чтобы не зацикливаться обратно в tun.
Ключевые модули
| Путь | Назначение |
|---|---|
core/Tun2Socks.kt, jni/tun2socks.c |
JNI-мост к нативному движку |
core/crypto/ShadowsocksCrypto.kt, ShadowsocksStream.kt |
AEAD (HKDF-SHA1, AES-GCM, ChaCha20-Poly1305), TCP-framing, UDP seal/open |
core/socks/LocalSocks5Server.kt |
Локальный SOCKS5-сервер: CONNECT + UDP ASSOCIATE |
core/upstream/* |
Апстримы SS / SOCKS5 / HTTP(S) |
service/ProxyVpnService.kt |
VpnService, whitelist, foreground-уведомление, статистика, журнал событий |
service/ProxyTileService.kt |
Тайл быстрых настроек (вкл/выкл из шторки) |
data/ConfigStore.kt, AppRepository.kt |
Персист конфигов (DataStore) и список приложений |
core/ConfigImport.kt |
Импорт ссылок ss:// (SIP002/legacy), socks5://, http(s):// |
ui/* |
Compose-экраны: Туннель (журнал событий на timeline rail), Серверы, Приложения, Опции |
Сборка
./gradlew :app:assembleRelease # → app/build/outputs/apk/release/app-release.apk (~2.4 МБ)
./gradlew :app:assembleDebug
Требуется Android SDK (platform-35, build-tools 35), NDK 27.2.x. Путь к SDK — в local.properties.
Оптимизация батареи
- Нативный event-loop tun2socks вместо userspace TCP/IP-стека на JVM (нет GC-давления на пакет).
- Блокирующий I/O на эластичном пуле потоков — простаивающие соединения паркуют поток (0% CPU).
setUnderlyingNetworks+ отслеживание смены сети,setMetered(false).- Низкоприоритетное foreground-уведомление, поллинг статистики раз в 2 с (без wakelock).
- Секундный тикер UI работает только при CONNECTED и только пока активити RESUMED.
Безопасность / ограничения
- Не реализованы VLESS/Reality (намеренно).
- Shadowsocks: AEAD-шифры (
aes-128-gcm,aes-256-gcm,chacha20-ietf-poly1305). Legacy stream-шифры и SS-2022 не поддерживаются. - HTTP/HTTPS-прокси не могут передавать UDP — для них работает только DNS-over-TCP.