Built-in Kotlin (kotlin-android removed), Java 11 compile options (compose 1.12),
proguard-android-optimize.txt (the non-optimize file is rejected by AGP 9),
ExposedDropdownMenu import, minSdk 23 kept via tools:overrideLibrary for
material3 1.5 libs that declare minSdk 24. -PshortNdkCommands for deep worktree paths.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop unused navigation-compose and material3-window-size-class.
alpha19+/beta01 need AGP 9.1 + compileSdk 37, so alpha18 is pinned.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Direct destinations that stay unreachable past the cooldown are now
routed through the VPN tunnel until the network changes. The Home
availability card marks such hosts "via tunnel" and offers a one-tap
"Via VPN" action for the rest.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
underlyingNetworkLabel() returned the first non-VPN network from
allNetworks, whose order is unspecified - Android keeps the cellular
network agent alive in parallel with Wi-Fi, so on Wi-Fi the details
sheet could show "Сотовая сеть". Wi-Fi now wins when present (same
precedence as NetworkMonitor's trust decision); cellular is only
reported when no Wi-Fi network exists.
- ConnectionDetailsSheet: session traffic split "via VPN / direct (RU)"
from the native engine counters (userspace mode only; system-routes
mode shows no split - the engine never sees that traffic), public exit
IP + country via cloudflare cdn-cgi/trace through the tunnel, current
underlying network (Wi-Fi + SSID / cellular)
- ConnectionCard: one-glance "N% direct" caption when the RU split moves
traffic directly
- dead direct hosts surfaced in UI: native snapshot of the negative-cache
table via new get_dead_targets (mutex-guarded, sorted by failures) +
JNI getDeadDirectTargets; section on the Routing tab and a badge in the
Home availability card naming dst, failure count, remaining cooldown
and the owning app (from the 2.13.15 owner-attribution cache)
- HomeViewModel: split/exit/deadTargets as separate hot flows; exit
refreshed with the probe round (60s cache), dead targets every round
minSdk 23 flipped AGP's useLegacyPackaging default to false (uncompressed
.so, extractNativeLibs=false): the APK grew 12 -> 23 MB. Updates are
downloaded over cellular, so pin useLegacyPackaging = true; APK is back
to ~12 MB (2.14.0 re-published with the same versionCode, sha updated in
the manifest before any device fetched it).
- minSdkVersion 21 -> 23 (device is Android 17, no other users); jni
Application.mk: APP_PLATFORM android-23 + -DHAVE_SIGWAITINFO (the NDK
sysroot declares sigwaitinfo since API 23, conflicting with strongSwan's
fallback prototype; the system implementation is used now)
- replace the bottom NavigationBar with NavigationSuiteScaffold (bottom bar
on phones, navigation rail in landscape/on tablets); item colors keep the
brand palette via NavigationSuiteItemColors(bar/rail/drawer)
- HomeScreen applies statusBarsPadding itself (the adaptive scaffold no
longer pads the content area)
- predictive back was already enabled (android:enableOnBackInvokedCallback)
Log-driven fixes from the 10-04 diag/charon analysis:
- direct_tcp: negative-cache window now escalates 45s x2 up to 5 min
(Telegram was opening ~3000 connects/h to each of two dead hosts,
waking the radio for every leaked 45s probe); skip the unconditional
fresh-socket retry on ECONNREFUSED (a refusal is deterministic on the
network the socket is bound to; roam-time sockets are already aborted
by reprotect)
- new dead-destination diag line (dst, fail count, cooldown) via JNI
callback when the window escalates or hits every 100th failure
- VpnStateService: double-start guard - a second connect() for the same
profile while a healthy attempt is in flight is ignored (four starters
raced through here within 20 s after the 2.13.14 self-update)
- NetworkMonitor/AutoConnectManager: flap detection (>=2 kind changes in
5 min stretches the auto-connect delay to 10 s; a Wi-Fi flap produced
a 7-second-lived session and a wasted IKE handshake)
- hardcode diag TAGs obfuscated by R8 ("a: update: up to date" ->
"UpdateManager: ..."), same for UpdateInstaller/VpnShortcutManager
Diag/charon logs from 2026-09-30 16:45 showed that after a Wi-Fi->cellular
roam every direct-TCP connect failed asynchronously with EHOSTUNREACH for
minutes, while the IKE socket worked and a Java socket explicitly bound to
the underlying network connected fine: a bare VpnService.protect() left
fresh sockets routed over the vanished network.
- CharonVpnService.protectDirect(fd): protect + Network.bindSocket to the
active non-VPN network (API 23+, best-effort, adoptFd/detachFd)
- charonservice_protect_direct_socket() JNI wrapper; relay and UDP flow
sockets use it at creation
- direct_tcp: one retry on a fresh socket when an async connect fails;
reprotect aborts still-connecting relays (RST) instead of letting the
kernel take ~90s to report the dead route; failures log dst=ip:port
- direct_udp: reprotect closes all flows; they re-open per packet with a
fresh, network-pinned socket
- TUN reader: EBADF after a reauth re-establish is no longer logged
Published as 2.13.10 (versionCode 147).
Replaces the fragment-based UI with a full Jetpack Compose app
(HOME/profiles/routing/settings/trusted networks in Compose, adaptive
brand theming, notification/vector drawables), adds the SHX release
pipeline (build-local.sh, UpdateManager self-update from the S3 bucket,
DiagLog diagnostics rotated into diag.log with crash capture) and the
watchdog/auto-connect/reconnect hardening series.
RU-GeoIP split now has two modes (Routing screen):
- OS routes (default): coarsened complement of the RU list installed
as system routes, bounded by the binder budget (2.12.x behavior)
- Userspace routing (beta): only the default routes point into the
TUN; every packet is classified natively against the exact RU list
(ring-buffer queue, fixed flow tables - allocation-free hot path)
and RU TCP/UDP leaves through protected sockets. TCP is terminated
by a vendored lwIP 2.2.0 (tun2socks model, err_t renamed to avoid
the strongSwan typedef clash, wildcard-port listener patch, asserts
into diag.log). IPv4 addresses are compared in Java's numeric byte
order and ranges sorted natively; direct flows re-protect on roam;
relay errors log errno. Route diagnostics block 12s after connect.
Also: SIM2-crash hardening, connectivity watchdog with wakeup alarm,
profile export, connectivity self-check screen, tests.
Published as 2.13.9 (versionCode 146).
- Added new entries to .gitignore to exclude Android build artifacts and keystore files.
- Updated build.gradle files to include Kotlin Gradle plugin dependencies.
- Enhanced gradle.properties with JVM arguments for better performance.
- Modified AndroidManifest.xml to include additional permissions for network and location access.
- Refactored package names in several classes to use a new namespace.
- Removed deprecated LogActivity, SelectedApplicationsActivity, SettingsActivity, and TrustedCertificatesActivity.
- Introduced a new scheduling mechanism in the Scheduler class to handle exact alarms on Android 13+.
- Updated MainActivity to integrate new UI components.
These changes improve the overall structure and functionality of the Android application.