Skip to content

feat: p2p wifi hotspot sync - android implementation#431

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

feat: p2p wifi hotspot sync - android implementation#431
paulpascal wants to merge 11 commits intomedic:masterfrom
paulpascal:feat/p2p-sync

Conversation

@paulpascal
Copy link
Copy Markdown
Contributor

@paulpascal paulpascal commented Apr 18, 2026

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

Status: 🔍 In Review

Development

Successfully merging this pull request may close these issues.

2 participants