Yaojia Wang
9114630c3a
feat(android): access-token support — same frozen contract as iOS
...
Android could not connect at all once the server set WEBTERM_TOKEN. Hand-written
Cookie header on every request and on the WS upgrade (no CookieJar, matching the
frozen decision), POST /auth pairing probe, Keystore-backed storage, and a 401
upgrade as a terminal state with no reconnect loop.
2026-07-30 12:45:27 +02:00
Yaojia Wang
e254918b1c
feat(android): native Android client — full app parity build (A1–A36 + S1)
...
relay-tripwire / cross-tenant-tripwire (push) Has been cancelled
ios / package-tests (APIClient) (push) Has been cancelled
ios / package-tests (HostRegistry) (push) Has been cancelled
ios / package-tests (SessionCore) (push) Has been cancelled
ios / package-tests (WireProtocol) (push) Has been cancelled
ios / testsupport-tests (push) Has been cancelled
ios / app-tests (push) Has been cancelled
ios / ipad-tests (push) Has been cancelled
ios / integration-tests (push) Has been cancelled
ios / ui-test (push) Has been cancelled
ios / ios17-floor-tests (push) Has been cancelled
Mirror the iOS P0+P1 client as a Gradle multi-module app. Pure-JVM (Kover ≥80% gated):
:wire-protocol (frozen contract + HostEndpoint CSWSH origin + byte-exact codec),
:session-core (SessionEngine + reconnect/ping/gate/digest reducers), :api-client,
:client-tls (pure PKCS12/keymanager), :transport-okhttp (OkHttp WS+REST). Framework:
:app (Compose M3 Adaptive, Hilt, FCM, 11 screens + NavGraph), :terminal-view (Termux
terminal-emulator/-view via JitPack, Apache-2.0 — the renderer seam proven headless),
:host-registry (DataStore), :client-tls-android (AndroidKeyStore + Tink).
Highlights: single-key-home mTLS (non-exportable AndroidKeyStore key + re-reading
X509KeyManager + connectionPool.evictAll on rotation, ping-pong single-commit so a failed
rotation never clobbers the prior identity); FCM Allow/Deny trust split (Deny=BroadcastReceiver,
Allow=trampoline Activity hosting BiometricPrompt); per-consumer-Channel EventBus (R10);
config-surviving RetainedSessionHolder; byte-exact KeyByteMap; §5.4 pairing warning tiers.
Verified: ~484 JVM tests + Kover ≥80% on the pure modules + :app assembles to an APK; all
framework modules assemble. Device behaviors (rendering/IME/FCM/biometric/camera, E2E A34/A35,
S2 real-handset FCM spike) deferred to android/DEVICE_QA_CHECKLIST.md per plan §7 (no
emulator/Firebase here). Built via multi-agent orchestration (TDD builders → adversarial
cross-review → fix → re-verify → independent gate); progress in android/PROGRESS_ANDROID.md.
2026-07-10 16:41:21 +02:00
Yaojia Wang
4ea8f7862a
feat(android): pure-Kotlin foundation — AW0 scaffold + AW1 modules (218 tests green)
...
Implements the verifiable pure-Kotlin core of the Android client per
docs/ANDROID_CLIENT_PLAN.md, mirroring the iOS SPM package set as Gradle modules.
Built + reviewed via multi-agent workflow (explore→implement→verify→review),
then review findings fixed with regression tests.
Modules (all pure JVM, kotlin("jvm"); Android-framework modules scaffolded but
gated off in settings — no Android SDK in this env):
- :wire-protocol — frozen wire contract (sealed Client/ServerMessage, enums,
HostEndpoint CSWSH origin derivation, transport interfaces), hand-rolled codec
byte-identical to the server's JSON.stringify + tolerant kotlinx decode.
- :session-core — ReconnectMachine (1→2→4→8→16→30 backoff), PingScheduler,
GateTracker (two-line epoch guard), AwayDigest, UnreadLedger, TitleSanitizer,
KeyByteMap (byte-matches public/keybar.ts).
- :api-client — all REST routes, tolerant decode, Origin-iff-guarded, prefs
unknown-key preservation, pairing probe + host-tier classifier.
- :client-tls — pure half: PKCS#12 parse, X509KeyManager alias logic,
CertificateSummary, provider-agnostic wrong-passphrase classification.
- :test-support — FakeTransport / FakeHttpTransport / virtual-clock fakes.
Verify: ./gradlew clean test → 218 tests, 0 failures (wire-protocol 47,
session-core 60, api-client 69, client-tls 27, test-support 15).
Review (3 lenses, 8/10 each) findings fixed: sessionId JSON-injection escape,
CancellationException propagation in PingScheduler, PairingProbe close-on-cancel
leak, HostEndpoint trim, HostClassifier hoisted to :wire-protocol (type unified),
BouncyCastle-portable wrong-passphrase detection, lone-surrogate escaping.
Known gap (documented, deferred): /push/fcm-token has no server route yet —
plan task A33 (src/push/fcm.ts) delivers it.
2026-07-08 13:45:07 +02:00