Skip to content

feat: p2p wifi hotspot sync - android implementation - #431

Open
paulpascal wants to merge 11 commits into
medic:masterfrom
paulpascal:feat/p2p-sync
Open

paulpascal wants to merge 11 commits into
medic:masterfrom
paulpascal:feat/p2p-sync

Conversation

@paulpascal

@paulpascal paulpascal commented Apr 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Android implementation of P2P WiFi Hotspot sync for CHT. Enables a Supervisor's phone to create a local WiFi hotspot and receive health data from CHW phones, without internet access.

This PR covers the full Android P2P module. The related server/webapp PR is at medic/cht-core#10893.

What's included

Foundation:

  • ScopeGuard : Deterministic document classification (IN_SCOPE / TRANSIT / REJECTED) based on facility hierarchy and replication depth
  • P2pAuthenticator + JwtVerifier — Offline JWT verification with ECDSA P-256
  • SyncMutex : Ensures only one sync (P2P or server) runs at a time
  • P2pConfig : Reads P2P configuration from app_settings

HTTP Server:

  • LocalHttpServer : NanoHTTPD-based HTTPS server running on the Supervisor's phone
  • 6 endpoints: auth, accept-docs, get-ids, bulk-get, get-deletes, status
  • AcceptDocsEndpoint : Receives docs, classifies via ScopeGuard, writes with new_edits: false

WiFi + Service:

  • WifiHotspotProvider : Android LocalOnlyHotspot (API 26+), isolated network with no internet routing
  • LoopbackHotspotProvider : Dev/emulator fallback
  • P2pForegroundService : Keeps hotspot alive during sync
  • OemBatteryHelper : Per-OEM battery optimization guidance

Transit + Tracking:

  • TransitDocManager : Tracks transit docs in _local/p2p-transit-docs
  • P2pTracker + P2pTelemetryReporter : Session metrics and telemetry

Orchestrator + Bridge:

  • P2pManager : Master orchestrator: capability check , permissions , hotspot , HTTP server , QR , sync , teardown
  • P2pBridgeMethods : 20 @JavascriptInterface methods for WebView communication
  • QrCodeHelper + QrScannerActivity : ZXing QR generation and scanning

Key design decisions:

  • NanoHTTPD : single file, MIT license, zero dependencies
  • LocalOnlyHotspot : standard API, no internet routing, consistent across OEMs
  • Roles are config-driven : no hardcoded role names in defaults
  • HotspotProvider interface enables easy transport extension (Bluetooth, file-based)

Files changed

  • 34 new Java classes in p2p/ package
  • 4 unit test classes
  • Append-only: AndroidManifest.xml, build.gradle, MedicAndroidJavascript.java, EmbeddedBrowserActivity.java
  • New: RequestP2pPermissionsActivity, permission layout XML, string resources

Wave 1 zero-dependency modules: ScopeGuard for doc classification
(IN_SCOPE/TRANSIT/REJECTED), JWT authentication with ECDSA P-256,
revocation list, sync mutex, P2P config reader, and session model.
Wave 2 core integration: NanoHTTPD local server with auth, get-ids,
bulk-get, accept-docs, get-deletes, and status endpoints. WiFi hotspot
management (production + loopback for dev). Foreground service, notification
channel, OEM battery helper. Session tracking, telemetry reporter,
transit doc manager with PouchDB bridge.
Wave 3 wiring: P2pManager orchestrates full sync lifecycle. P2pBridgeMethods
exposes @JavascriptInterface to WebView. QR code generation (Supervisor) and
scanning (CHW). P2pSyncClient for CHW-side HTTP sync. Permission activity for
camera/WiFi/location. Integrates into build.gradle (NanoHTTPD + ZXing deps),
AndroidManifest.xml, MedicAndroidJavascript bridge, and EmbeddedBrowserActivity.
Wave 4 tests: ScopeGuard classify() determinism, P2pAuthenticator JWT
verification, SyncMutex concurrency, TransitDocManager lifecycle.
Default allowed roles changed from ['chw', 'chw_supervisor'] to empty
list. Roles must come from app_settings p2p_sync config. Removed
hardcoded 'chw_supervisor' string in tracker session start.
- Add serialVersionUID to JwtVerificationException and P2pInitException
- Use try-with-resources in P2pSyncClient.readBody()
- Suppress CloseResource in LocalHttpServer (stream owned by NanoHTTPD)
- Fix CallSuperLast in QrScannerActivity.onSaveInstanceState()
- Fix EmptyCatchBlock in P2pAuthenticatorTest
- Extract string constants to reduce duplication (S1192)
- Reduce cognitive complexity by extracting helpers (S3776)
- Replace generic Exception catches with specific types (S112)
- Use Builder pattern for P2pConfig constructor (S107)
- Introduce ServerDeps/HotspotCredentials to reduce param counts (S107)
- Add synchronized to getState() matching setState() (S2886)
- Remove unused imports (S1128)
- Fix int overflow in time calculations (S2184)
- Handle InterruptedException properly (S2142)
- Merge switch cases (S6208)
- JwtVerifier: replace switch with if, add default, use specific exception
- P2pAuthenticator: use pattern matching instanceof
- RevocationList: extract helpers for complexity
- ScopeManifest: extract helpers, use pattern matching instanceof
- StatusEndpoint: use specific exception
- TransitDocManager: remove unused import, fix int overflow, extract helpers,
  extract constants for duplicated literals
AcceptDocsEndpoint: extract KEY_PARENT constant, break doHandle/hydrateParentLineage/
fetchAndCacheParentChains/classifyTransitDocs into helpers, remove unused param
ScopeGuard: extract constants, break classify into helpers, remove throws JSONException
WifiHotspotProvider: extract helpers, fix InterruptedException handling, add logging
LocalHttpServer: consolidate constructors, extract helpers
P2pBridgeMethods: extract 7 constants, extract nested try blocks into methods,
remove commented code, reduce complexity
P2pManager: remove unused params, extract constant, merge nested if
QrCodeHelper: use EnumMap, extract validation helpers
OemBatteryHelper: data-driven brand guidance
AuthEndpoint/P2pConfig: extract helpers
Tests: assertNull, add assertions, suppress parameterized test suggestions
@paulpascal paulpascal changed the title feat: P2P WiFi Hotspot sync — Android implementation feat: p2p wifi hotspot sync - android implementation Apr 18, 2026
@paulpascal
paulpascal force-pushed the feat/p2p-sync branch 9 times, most recently from bbea9a8 to 3163fc4 Compare April 18, 2026 23:41
- Convert all indentation to tabs (cht-android convention)
- Move string concatenation operators to end of previous line (OperatorWrap)
- Fix ParenPad violation in LocalHttpServer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

No open projects
Status: 🔍 In Review
Status: 🔍 In Review

Development

Successfully merging this pull request may close these issues.

2 participants