4.1 KiB
4.1 KiB
Building & testing
Prerequisites & first run
- Android SDK platform-35 + build-tools 35, and the NDK version pinned in
app/build.gradle.kts(ndkVersion). Pointlocal.properties(sdk.dir=…) at the SDK. - The Gradle wrapper is bundled (
./gradlew, currently Gradle 8.10.2) — no system Gradle needed. - First device run:
./gradlew :app:installDebug(orassembleDebugthenadb install). The tunnel itself can't be shell-started (see on-device QA) — tap «Подключиться» in the UI.
Build
./gradlew :app:assembleDebug # debug APK (id chat.vojo.proxy.debug)
./gradlew :app:assembleRelease # minified + shrunk; signed with the bundled debug keystore
minSdk 28,targetSdk/compileSdk 35, single ABI arm64-v8a.- Native build is ndk-build via
app/src/main/jni/Android.mk, which includes the vendoredhev-socks5-tunnel/Android.mkand buildstun2socks.cintolibtun2socks.so.ndkVersionis pinned inapp/build.gradle.kts.jniLibsare kept uncompressed + 16 KB-aligned.
Three test layers
1. JVM unit tests (committed, CI-friendly) — app/src/test/
./gradlew :app:testDebugUnitTest
ShadowsocksCryptoTest— SIP004 known-answer tests (master key, HKDF subkey, AES-128/256 GCM ciphertext — all cross-checked against shadowsocks-rust/sing-shadowsocks and recomputed in python), round-trips, wrong-key failure, the zero-length-chunk regression.ConfigImportTest— share-link parser (the percent-decode and bare-IPv6 fixes, scheme dispatch). Base64ss://paths are stubbed on the JVM; those are covered by the harness/device.unitTests.isReturnDefaultValues = truelets the fewandroid.util.Base64calls return null instead of throwing the "Stub!" error. ChaCha20 is not unit-tested (desktop SunJCE has noChaCha20/Poly1305provider — it runs via Conscrypt on device + in the harness).
2. Interop harness (local only, gitignored) — .harness/
A standalone JVM rig that compiles vendored copies of the upstream Kotlin and runs them against Python reference servers (real SOCKS5 / HTTP(S) / Shadowsocks).
bash .harness/run.sh # all green (0 FAIL); run.sh is the source of truth
- Covers things unit tests can't: real AEAD round trips through a reference SS server, SOCKS5 UDP ASSOCIATE incl. wrong-source-drop, HTTP CONNECT 407/bare-LF, HTTPS hostname verification, the post-connect probe classification.
- Drift caveat:
.harness/src/*.ktare copies of the real source. If you change upstream code (core/upstream,core/crypto,core/net), copy the changed file into.harness/src/before trusting a green run..harness/is gitignored (it ships throwaway test-CA keys and build artifacts).
3. On-device QA — Galaxy S23 over network ADB
The debug build is debuggable, so config can be injected without tapping through the UI:
- VPN consent without the dialog:
adb shell appops set chat.vojo.proxy.debug ACTIVATE_VPN allow→VpnService.prepare()returns null. - Inject a config: build the Preferences-DataStore protobuf yourself (one entry, key
state= theProxyStateJSON) and write it viarun-as, piping base64 through stdin (run-as can't read/sdcard). Force-stop the app first (it caches DataStore in memory). - Start the tunnel: the service is not shell-startable (
BIND_VPN_SERVICE, not exported) — tap «Подключиться» in the UI (adb shell input tap …). - Exit-IP / leak probe:
adb shell curl -s https://1.1.1.1/cdn-cgi/trace→ theip=line. Through the tunnel it's the proxy's exit IP; after disconnect it must return to the WAN IP. Usecurl -w '%{time_namelookup}'to watch DNS latency (the old UDP-relay bug showed as ~6 s). - Per-app gotcha: to probe via
adb shell curl, the shell UID must be routed — i.e. setallowedAppsempty (route all) for the test, or the probe bypasses the tunnel and looks like a leak. - WiFi-ADB drops are the phone's Wi-Fi power-save on screen-sleep, not WSL. Keep the screen
awake (
settings put global stay_on_while_plugged_in 3) or use USB via usbipd.