feat(android): приложение под Android и переход на rustls

Android грузит приложение как нативную библиотеку и входит через JNI-символ, а
не через main. Поэтому Tauri-приложение переехало из src/main.rs в src/gui.rs
внутри библиотеки, с #[cfg_attr(mobile, tauri::mobile_entry_point)], а main.rs
стал обёрткой. Модуль объявлен как #[cfg(feature = "gui")], так что свойство
«--no-default-features даёт ядро без Tauri и WebView» сохранилось — проверено
сборкой и 51 тестом headless-варианта.

Переход на rustls — вынужденный, но выгодный. native-tls на Linux и Android
тянет OpenSSL, а openssl-sys не кросскомпилируется под aarch64-linux-android:
сборка падала на нём. rustls на чистом Rust, корневые сертификаты webpki вшиты
в бинарь. Побочно: на Linux исчезла зависимость от системного libssl, а образу
Docker больше не нужен даже ca-certificates.

На этом переходе тест живой сети поймал баг, который прошёл бы в релиз: rustls
0.23 отказывается угадывать криптопровайдер и ПАНИКУЕТ на первом TLS-
рукопожатии. Компиляция чистая, все офлайновые тесты зелёные — они ходят через
локальный маршрут без TLS. То есть в сборку ушло бы приложение, не способное
подключиться ни к чему. Провайдер (ring, кросскомпилируется под Android)
устанавливается в ensure_crypto_provider, регрессию держит тест
a_crypto_provider_is_available_for_tls. Живой тест против шести боевых
дата-центров Telegram проходит.

Прочее:
- open::that заменён на tauri-plugin-opener: у крейта open нет реализации под
  Android, а плагин работает на обеих платформах.
- Библиотека переименована в tglock_lib: одинаковые имена lib и bin давали
  коллизию выходных файлов, которую cargo обещает сделать ошибкой.
- Программный рендер WebView не применяется на Android — там GPU есть всегда,
  и переопределение только замедлило бы интерфейс.
- .gitignore: строка /gen скрывала весь сгенерированный Android-проект. Теперь
  игнорируются только артефакты сборки, local.properties и keystore.

Foreground service. Прокси — поток в процессе приложения, и без сервиса система
выгрузит его через минуты после сворачивания. Сервис стартует из MainActivity,
а не по команде из ядра: JNI-мост между Rust и Kotlin осознанно не делался,
чтобы не добавлять слой, который нельзя проверить. Плата — уведомление висит,
пока открыто приложение, даже при выключенной защите.

НЕ ПРОВЕРЕНО: вся Android-часть в рантайме. Устройств не подключено,
эмулятора и системных образов в SDK нет. APK собирается, но приложение никто
ни разу не запускал: ни интерфейс, ни сервис, ни тип specialUse на Android 14+,
ни запрос разрешения на уведомления. Поэтому Android-артефакт сознательно НЕ
добавлен в release.yml — публиковать нечего, пока никто не запустил это на
живом устройстве.
This commit is contained in:
by-sonic
2026-07-30 15:19:24 +03:00
parent 59b9cdd68c
commit a87e26c581
51 changed files with 2075 additions and 480 deletions
+13
View File
@@ -91,6 +91,19 @@ LAN-режим превращал бы машину в открытый прок
заданному Telegram IP TCP destination отделён от URI host: TLS продолжает
проверять сертификат настоящего `kws*.web.telegram.org`.
Стек — **rustls** с корневыми сертификатами webpki, вшитыми в бинарь. Выбран не
из вкуса: `native-tls` на Linux и Android тянет OpenSSL, а `openssl-sys` не
кросскомпилируется под `aarch64-linux-android` без сборки OpenSSL вручную.
Побочные выгоды: на Linux исчезла зависимость от системного `libssl`, а образу
Docker больше не нужен даже `ca-certificates`.
Криптопровайдер rustls выбирается явно (`ring`). Без этого rustls 0.23
отказывается угадывать и **паникует на первом TLS-рукопожатии** — это не
ошибка компиляции и её не видят офлайновые тесты, потому что они ходят через
локальный маршрут без TLS. Провайдер устанавливается в `ensure_crypto_provider`
перед подключением, а тест `a_crypto_provider_is_available_for_tls` не даёт
регрессии вернуться.
Cloudflare Worker принимается только как пользовательская настройка. TGLock
не загружает и не скрывает публичные списки чужих доменов.