diff --git a/Android/app/build.gradle.kts b/Android/app/build.gradle.kts index 932fc67c..91d4a278 100644 --- a/Android/app/build.gradle.kts +++ b/Android/app/build.gradle.kts @@ -11,8 +11,8 @@ android { applicationId = "com.archipelago.app" minSdk = 26 targetSdk = 35 - versionCode = 23 - versionName = "0.5.3" + versionCode = 24 + versionName = "0.5.4" vectorDrawables { useSupportLibrary = true diff --git a/Android/app/src/main/java/com/archipelago/app/fips/ArchyVpnService.kt b/Android/app/src/main/java/com/archipelago/app/fips/ArchyVpnService.kt index 36714e05..25a38d2c 100644 --- a/Android/app/src/main/java/com/archipelago/app/fips/ArchyVpnService.kt +++ b/Android/app/src/main/java/com/archipelago/app/fips/ArchyVpnService.kt @@ -68,6 +68,14 @@ class ArchyVpnService : VpnService() { // And let apps that bind their own network skip the TUN // entirely — this is mesh reachability, not a privacy VPN. .allowBypass() + .apply { + // Android 10+ treats VPN networks as METERED by default, + // which flips the whole phone into data-saver behaviour + // (background sync off, "metered" warnings) while the + // mesh is up. It inherits the underlying network's real + // metered state instead. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) setMetered(false) + } .establish() } catch (e: Exception) { Log.e(TAG, "VPN establish failed", e) diff --git a/Android/rust/archy-fips-core/Cargo.toml b/Android/rust/archy-fips-core/Cargo.toml index 2fb414d6..199d4517 100644 --- a/Android/rust/archy-fips-core/Cargo.toml +++ b/Android/rust/archy-fips-core/Cargo.toml @@ -31,6 +31,8 @@ hex = "0.4" getrandom = "0.2" tokio = { version = "1", features = ["rt-multi-thread", "sync", "time", "macros"] } tracing = "0.1" +# fcntl: force the VpnService TUN fd into blocking mode (see mesh::start). +libc = "0.2" # The JNI surface only exists on Android; host builds skip it and drive the # mesh module directly (tests). diff --git a/Android/rust/archy-fips-core/src/mesh.rs b/Android/rust/archy-fips-core/src/mesh.rs index 9b1b05b7..2a76090a 100644 --- a/Android/rust/archy-fips-core/src/mesh.rs +++ b/Android/rust/archy-fips-core/src/mesh.rs @@ -97,6 +97,19 @@ pub fn parse_peers(peers_json: &str) -> Result> { pub fn start(secret: &str, peers_json: &str, tun_fd: i32) -> Result<(String, String)> { stop(); + // Android hands the VpnService TUN fd over in non-blocking mode on some + // OS builds. The fips TUN reader is a dedicated blocking-read thread that + // treats EAGAIN as fatal — the loop died at startup ("TUN read error … + // Try again (os error 11)" on-device), so sessions came up but no packet + // ever entered the mesh. Force the fd into the blocking mode the reader + // is designed for. + unsafe { + let flags = libc::fcntl(tun_fd, libc::F_GETFL); + if flags >= 0 && (flags & libc::O_NONBLOCK) != 0 { + libc::fcntl(tun_fd, libc::F_SETFL, flags & !libc::O_NONBLOCK); + } + } + let peers = parse_peers(peers_json)?; let config = build_config(secret, peers); let mut node = Node::new(config).map_err(|e| anyhow!("node init: {e}"))?; diff --git a/docs/HANDOFF-2026-07-23-companion-apk-deploy.md b/docs/HANDOFF-2026-07-23-companion-apk-deploy.md index 0bcf3ec9..fcf67854 100644 --- a/docs/HANDOFF-2026-07-23-companion-apk-deploy.md +++ b/docs/HANDOFF-2026-07-23-companion-apk-deploy.md @@ -48,9 +48,40 @@ user's hands. verify `sudo -n fipsctl show status` reports the anchor connected — `fips.reconnect` RPC if not. A phone can dial the anchor perfectly and still fail if the node never enrolled with it. -5. Re-test the user's exact flows with vc23 (updates any older install in +5. Re-test the user's exact flows with **vc24** (updates any older install in place): (a) pair ON the LAN, then switch the phone to 5G — the UI must come up via the mesh ULA; (b) pair while ALREADY on 5G (never on the node's LAN) — scan, VPN consent, and the connect must succeed through - the anchor. If either fails, capture `adb logcat` around the attempt and - `fipsctl show sessions` on the node, and report back. + the anchor. + +## Live diagnosis update (22:30–22:50, phone on adb — Mac agent) + +vc23 on-device testing found and fixed the phone-side blocker, and narrowed +what remains to the node side. State as of vc24: + +- **Fixed: TUN reader died at startup.** Android hands the VpnService fd over + non-blocking; the fips fork's blocking reader thread treats EAGAIN as fatal + ("TUN read error … Try again (os error 11)") — so mesh sessions came up but + NO packet ever entered the tunnel. archy-fips-core now forces the fd + blocking before `start_with_tun_fd`. Verified on-device: reader survives, + and the 30s anchor-link flap disappeared with it (stable 8+ min on 5G). +- **Fixed: VPN marked not-metered** (`setMetered(false)`) — Android 10+ + defaults VPNs to metered, putting the phone into data-restricted behaviour + while the mesh is up. Note the user's phone also has system **always-on + VPN** enabled for the app (`always_on_vpn_app`), a Settings-side toggle. +- **Verified good on-device**: peer store has node (LAN udp/tcp hints) + vps2 + anchor; saved server is npub-keyed with ULA; anchor session establishes + from 5G in ~6s; VPN is bypassable, VALIDATED, only fd00::/8 routed. +- **REMAINING BLOCKER (node side)**: from the phone (app uid), ping6 and + HTTP to the node's ULA `fd79:1aa:b9e9:4c9f:1f80:5376:9385:1824` get no + reply — packets enter the mesh, nothing returns. Phone↔anchor works, so + suspect phone-fork ↔ node-daemon session/routing mismatch (FIPS wire + format is not stable across revs; phone pins fips-native fork 46494a74). + From Framework PT please capture: + - `fipsctl show status` (daemon version + anchor state) + - `fipsctl show sessions` and `show bloom` while the phone pings + - `ping6 ` from the + node (tests the reverse path) + - `ip addr show fips0` + confirm the web server listens on `[::]:80` + Report whether the node ever sees a session attempt from + `npub132c5whrsa6ccs0eylcpzaejq9uxul5ldvczz0axq78dh7fxkqj9st4uvzu`. diff --git a/neode-ui/public/packages/archipelago-companion.apk b/neode-ui/public/packages/archipelago-companion.apk index 3d9e0437..03563c93 100644 Binary files a/neode-ui/public/packages/archipelago-companion.apk and b/neode-ui/public/packages/archipelago-companion.apk differ