android: Full Compose rewrite + two-mode RU split with userspace routing
Android / pre-check (push) Failing after 5s
Android / android (push) Skipped
CIFuzz / pre-check (push) Failing after 3s
CIFuzz / fuzzing (address) (push) Skipped
CIFuzz / fuzzing (undefined) (push) Skipped
CodeQL / pre-check (push) Failing after 3s
CodeQL / analyze (python) (push) Skipped
CodeQL / analyze (ruby) (push) Skipped
CodeQL / analyze-cpp (push) Skipped
FreeBSD / pre-check (push) Failing after 3s
FreeBSD / freebsd (14.4) (push) Skipped
FreeBSD / freebsd (15.0) (push) Skipped
Linux / pre-check (push) Failing after 3s
Linux / latest (apidoc) (push) Skipped
Linux / latest (clang, no, all) (push) Skipped
Linux / latest (clang, no, default) (push) Skipped
Linux / latest (clang, no, printf-builtin) (push) Skipped
Linux / latest (clang, no-dbg) (push) Skipped
Linux / latest (clang, no-testable-ke) (push) Skipped
Linux / latest (clang, yes, all) (push) Skipped
Linux / latest (clang, yes, default) (push) Skipped
Linux / latest (clang, yes, fuzzing) (push) Skipped
Linux / latest (coverage) (push) Skipped
Linux / latest (dist) (push) Skipped
Linux / latest (gcc, no, all) (push) Skipped
Linux / latest (gcc, no, default) (push) Skipped
Linux / latest (gcc, no, printf-builtin) (push) Skipped
Linux / latest (gcc, yes, all) (push) Skipped
Linux / latest (gcc, yes, default) (push) Skipped
Linux / latest (nm) (push) Skipped
Linux / latest (no-dbg) (push) Skipped
Linux / latest (no-testable-ke) (push) Skipped
Linux / latest (yes, ld) (push) Skipped
Linux / crypto (ubuntu-22.04, openssl-sys) (push) Skipped
Linux / crypto (ubuntu-latest, botan) (push) Skipped
Linux / crypto (ubuntu-latest, openssl-3) (push) Skipped
Linux / crypto (ubuntu-latest, openssl-4) (push) Skipped
Linux / crypto (ubuntu-latest, openssl-awslc) (push) Skipped
Linux / crypto (ubuntu-latest, openssl-sys) (push) Skipped
Linux / crypto (ubuntu-latest, wolfssl) (push) Skipped
Linux / older (clang, ubuntu-22.04, all) (push) Skipped
Linux / older (gcc, ubuntu-22.04, all) (push) Skipped
Linux / older (gcc, ubuntu-22.04, nm) (push) Skipped
Linux / alpine (push) Skipped
macOS / pre-check (push) Failing after 3s
macOS / macos (macos-14) (push) Skipped
macOS / macos (macos-latest) (push) Skipped
SonarCloud / pre-check (push) Failing after 4s
SonarCloud / sonarcloud (push) Skipped
TKM / pre-check (push) Failing after 3s
TKM / tkm (push) Skipped
Windows / pre-check (push) Failing after 4s
Windows / cross-compile (win32) (push) Skipped
Windows / cross-compile (win64) (push) Skipped
Windows / native (i686, mingw32, win32) (push) Skipped
Windows / native (x86_64, mingw64, win64) (push) Skipped

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).
This commit is contained in:
Denozordec committed 2026-09-30 16:50:03 +07:00
1 parent 9dcc56c821
commit d12fb96dc2
494 files changed
+156372 -5520

No files matched your search

@@ -30,6 +30,14 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<!-- required to install downloaded updates of the app itself -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!-- lets the app install its own updates in the background without the
system confirmation dialog (Android 12+, same package/signature only) -->
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
<!-- required to claim update ownership (setRequestUpdateOwnership) so our
own updates keep being applied silently on Android 14+ -->
<uses-permission android:name="android.permission.ENFORCE_UPDATE_OWNERSHIP" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
@@ -53,6 +61,7 @@
android:allowBackup="false" >
<activity
android:name=".ui.MainActivity"
android:theme="@style/ShxSplashTheme"
android:label="@string/main_activity_name"
android:launchMode="singleTop"
android:exported="true">
@@ -79,6 +88,17 @@
<action android:name="one.shx.strongswan.ext.action.DISCONNECT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="one.shx.strongswan.ext.action.TOGGLE_VPN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<!-- deep links for automation tools: shxvpn://connect[?profile=<uuid>], shxvpn://disconnect, shxvpn://toggle -->
<intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="shxvpn" />
</intent-filter>
</activity>
<activity
android:name=".ui.VpnProfileDetailActivity" >
@@ -103,10 +123,13 @@
android:name=".ui.TrustedNetworksActivity"
android:label="@string/auto_connect_title">
</activity>
<activity
android:name=".ui.ConnectivityCheckActivity"
android:label="@string/health_check_title">
</activity>
<activity
android:name=".ui.RemediationInstructionsActivity"
android:label="@string/remediation_instructions_title"
android:theme="@style/ApplicationTheme.ActionBar" >
android:label="@string/remediation_instructions_title" >
</activity>
<activity
android:name=".ui.VpnProfileSelectActivity"
@@ -225,6 +248,41 @@
not work (probably too many indirections with ACTION_SEND) so we secure
this provider with a custom ticketing system -->
</provider>
<provider
android:name=".data.ProfileExportProvider"
android:authorities="one.shx.strongswan.ext.content.export"
android:exported="true" >
<!-- same ticketing approach as above for shared VPN profile exports -->
</provider>
<!-- serves downloaded updates to the system package installer -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<!-- receives the result of the self-update installation -->
<receiver
android:name=".logic.updates.UpdateInstallStatusReceiver"
android:exported="false" />
<!-- restores an expected VPN connection after the hosting process died;
only uses a non-wakeup alarm so it never wakes the device -->
<receiver
android:name=".logic.WatchdogReceiver"
android:exported="false" />
<!-- runs after the app has updated itself (the system kills the
process during the install): cleans up and lets auto-connect
restore the VPN -->
<receiver
android:name=".logic.updates.UpdateReplacedReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
</application>
</manifest>