This commit is contained in:
CC
2026-06-10 14:14:28 +02:00
parent 2bd2751a6a
commit fc38a8c6b6
57 changed files with 1157 additions and 621 deletions
Binary file not shown.
+69
View File
@@ -0,0 +1,69 @@
# Mesh Core Refactor Plan
Goal: unify BLE + Wi-Fi Aware mesh services behind a shared core to reduce duplication, improve readability, and make auditing easier without changing runtime behavior.
## Phase 0: Inventory + Guardrails
- [x] Capture current public API surface used by UI/services (identify required methods/properties).
- [x] Identify transport-specific responsibilities vs shared mesh-core logic.
- [x] Decide how to share GossipSyncManager (keep existing holder behavior unless explicitly improved).
## Phase 1: Shared Types + Utilities
- [x] Introduce `MeshDelegate` in `com.bitchat.android.mesh` and make both BLE/WiFi delegates type-alias it.
- [x] Add `MeshTransport` interface to abstract local transport send/unicast and optional info hooks.
- [x] Extract `MeshPacketUtils` (hex id parsing, sha256 helper) used by both services.
- [x] Add unit tests for `MeshPacketUtils` (hex parsing + sha256 stability).
## Phase 2: MeshCore (Shared Coordinator)
- [x] Create `MeshCore` with shared managers (Peer/Fragment/Security/StoreForward/Message/Packet).
- [x] Move shared delegate wiring into `MeshCore` with hooks for transport-specific behavior:
- onMessageReceived (BLE notification / AppStateStore)
- onPeerIdBinding (BLE favorites mapping)
- onAnnounceProcessed (BLE first-announce address map)
- [x] Centralize shared packet signing + announce/leave creation.
- [x] Centralize message/file/read-receipt senders (public + private).
- [x] Expose helper methods for peer info, session state, debug info, and panic-mode clearing.
## Phase 3: BluetoothMeshService Integration
- [x] Replace duplicated core fields with a single `MeshCore` instance.
- [x] Wire `BluetoothConnectionManager` callbacks into `MeshCore` packet ingestion.
- [x] Move periodic announce + gossip start/stop into `MeshCore` usage.
- [x] Preserve BLE-only behaviors (connection manager control, background notifications).
- [x] Keep `MeshServiceHolder` semantics intact (reusability + shared gossip manager).
## Phase 4: WifiAwareMeshService Integration
- [x] Replace duplicated core fields with a `MeshCore` instance.
- [x] Route WiFi socket RX into `MeshCore` packet ingestion.
- [x] Keep WiFi aware discovery/connection logic transport-specific.
- [x] Ensure gossip sync usage matches current shared behavior.
## Phase 5: Cleanup + Consistency
- [x] Remove legacy duplicate helpers + delegates from both services.
- [x] Ensure debug status output stays consistent (core + transport sections).
- [x] Confirm behavior parity for announce/leave + handshake flows.
## Phase 6: Tests + Verification
- [x] Run unit tests: `./gradlew test` (or targeted if full suite is too slow).
- [x] If new tests fail, iterate until green.
## Status Tracking
- [x] Phase 0 complete
- [x] Phase 1 complete
- [x] Phase 2 complete
- [x] Phase 3 complete
- [x] Phase 4 complete
- [x] Phase 5 complete
- [x] Phase 6 complete
## Post-Refactor Review Findings (2025-02-XX)
Findings (confirmed issues to fix):
- Shared `GossipSyncManager` is stopped when WiFi Aware stops, even if BLE is still active.
- Shared `GossipSyncManager` unicast (`sendPacketToPeer`) only reaches BLE peers; WiFi Aware peers miss initial sync.
- Read receipts are sent only via BLE in UI paths; WiFi Aware-only sessions never send receipts.
TODO (fix plan):
- [x] MeshCore: guard `gossipSyncManager.start/stop` so only the owner transport controls lifecycle.
- [x] TransportBridgeService: add unicast forwarding across transports; update BLE/WiFi transport layers to support it.
- [x] MeshCore: when owning gossip manager, forward unicast sync packets to other transports via bridge.
- [x] UI: route read receipts via BLE if session established, else via WiFi Aware when available.
- [x] Tests: add unit tests for `TransportBridgeService` unicast forwarding and run them.
+18
View File
@@ -0,0 +1,18 @@
# WiFi Aware + BLE Mesh Fix Plan
## Goals
- One canonical, merged peer list across BLE + WiFi Aware.
- Packets relay seamlessly across transports without flapping or missing peers.
- ANNOUNCE and peer mapping remain consistent, regardless of transport.
## TODO Tracker
- [x] Add a centralized mesh peer registry and merge BLE + WiFi Aware peer lists.
- [x] Route peer list updates through the registry (BLE + WiFi Aware).
- [x] Remove UI state overwrites of connected peers; use merged list for cleanup/notifications/outbox.
- [x] Bridge relay across transports (BLE → WiFi Aware and WiFi Aware → BLE).
- [ ] Verify peer list stability and crosstransport routing with mixed peers.
## Implementation Notes
- Registry should own the merged list and update `AppStateStore` only from the merged result.
- Relay bridging should occur at `PacketProcessorDelegate.relayPacket(...)` to keep relay logic centralized.
- Avoid new loops/duplication: reuse existing TTL/relay rules and do not mutate the routed packet.
+189
View File
@@ -0,0 +1,189 @@
**Summary**
I reviewed the background/foreground service branch against `main` focusing on mesh, relay, DMs, handshake and lifecycle. Overall the architecture moves the mesh into a persistent Foreground Service with a notification and centralizes a single `BluetoothMeshService` via a processwide holder. The BLE layer and power manager refactors look solid.
I found several lifecycle and policy gaps that can break behavior when the app is in the background or is killed, and a couple of leaks/overengineering hotspots. Below are concrete issues and recommendations with file references.
**Key Risks and Issues**
- Foreground toggle doesnt control mesh start/stop
- `MeshForegroundService.ensureMeshStarted()` starts the mesh regardless of the “background enabled” preference or notification permission.
- Impact: Mesh can run without a visible foreground notification (policy risk) or continue running after the user disables background, causing hidden BLE activity.
- Where:
- `app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt:195` (ensureMeshStarted only checks BT perms)
- `app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt:176` (loop updates notif but never stops mesh when disabled/perms missing)
- ACTION_STOP doesnt stop the mesh
- The STOP action stops only the foreground service and not the BLE mesh.
- Impact: BLE continues to scan/advertise without a notification, violating FGS expectations and confusing users.
- Where: `app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt:133` (STOP path does not call `meshService.stopServices()`)
- Service destroy doesnt clean up the mesh
- `onDestroy()` cancels a job but does not stop the mesh or clear the holder.
- Impact: Potentially leaves the mesh running if the FGS is torn down (e.g., the system stops the service), and can lead to a stale singleton.
- Where: `app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt:234`
- Possible foreground service policy violation window
- In the periodic loop, if background is disabled or POST_NOTIFICATIONS is missing, the code stops the foreground state and cancels the notification, but it does not stop the mesh.
- Impact: Mesh continues running in background with no notification. This is high risk for Play policy and Android OS behavior (more aggressive kills).
- Where: `app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt:182-191`
- BluetoothPacketBroadcaster actor scope leak
- `BluetoothPacketBroadcaster` creates its own `broadcasterScope` with a fresh `SupervisorJob()` instead of using the connection scope passed into the manager. Its never cancelled in `stopServices()`.
- Impact: Actor and jobs can outlive the connection manager/service, causing leaks and unexpected broadcast attempts.
- Where:
- `app/src/main/java/com/bitchat/android/mesh/BluetoothPacketBroadcaster.kt:55` (ctor receives a scope)
- `app/src/main/java/com/bitchat/android/mesh/BluetoothPacketBroadcaster.kt:98` (creates new `broadcasterScope` ignoring the injected scope)
- No corresponding cancellation on stop in `BluetoothConnectionManager.stopServices()`.
- Boot completed auto-start edge cases
- Auto-start runs on BOOT_COMPLETED and LOCKED_BOOT_COMPLETED.
- On Android 13+, if POST_NOTIFICATIONS isnt granted, `MeshForegroundService.start()` wont start (correct), but theres no fallback scheduling (e.g., WorkManager) to retry foreground promotion once the user unlocks or grants permission later.
- Impact: Users expecting auto-start may never get the mesh until they open the app once or grant notifications via a separate action.
- Where:
- `app/src/main/java/com/bitchat/android/service/BootCompletedReceiver.kt:10-19`
- `app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt:37-63` (strict start gating on notif permission)
- Multiple starters and race potential
- Both `BitchatApplication` and `MainActivity` attempt to start the foreground service and/or get/create the mesh.
- Impact: It works because the holder dedupes instances and `startServices()` is idempotent, but the responsibility boundaries are muddled and harder to reason about; risk of start during missing perms or unwanted background.
- Where:
- `app/src/main/java/com/bitchat/android/BitchatApplication.kt:42-46`
- `app/src/main/java/com/bitchat/android/MainActivity.kt:47-67`
- Mesh can run without an attached UI delegate
- Intentional, and DM notifications are handled via `serviceNotificationManager` if no UI delegate is present.
- Risk: Good for background DMs, but be sure delivery acks/read receipts dont depend on UI-only state.
- Where:
- `app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt:391-411` (use of `serviceNotificationManager` when delegate is null)
- Android 14 FGS restrictions timing window
- `startForegroundService()` requires calling `startForeground()` within ~5 seconds. Primary start path builds the notification right away, but the retry/promotion logic in the update loop could call `startForeground()` multiple times or after a delay.
- Impact: While caught in try/catch, repeated `startForeground()` attempts arent ideal; more deterministic promotion improves reliability.
- Where:
- `app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt:168-174` (initial call OK)
- `app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt:178-187` (repeated attempts in loop)
- Over-permissive defaults
- Defaults enable background and auto-start. Without an explicit UX flow, users may unexpectedly run BLE activity until they disable background.
- Recommendation: Default background OFF on fresh installs, and require explicit optin.
- Where:
- `app/src/main/java/com/bitchat/android/service/MeshServicePreferences.kt:20-32`
- Minor: Notification permission “grant” handling depends on an inapp broadcast
- `NotificationPermissionChangedReceiver` listens for `ACTION_NOTIFICATION_PERMISSION_GRANTED` but depends on some component actually broadcasting it.
- Impact: If no component sends the broadcast after permission grant, the FGS wont autopromote.
- Where:
- `app/src/main/java/com/bitchat/android/service/NotificationPermissionChangedReceiver.kt:10-18`
**What Might Not Work Reliably In Background**
- Packet relay and scanning without a persistent FGS
- If background preference is disabled or notification permission is missing, the mesh can still be started by `ensureMeshStarted()` and keep running without a foreground notification. Android may restrict scan/advertise in Doze, leading to reduced connectivity, delayed DMs, and poor relay propagation.
- “Stop service” paths
- Using the STOP notification action does not stop the mesh; users think they stopped the app, but BLE stays active. This can cause unexpected background behavior and policy problems.
- Restart after process kill or reboot
- Reboot auto-start requires notification permission; otherwise it silently does nothing. Theres no queued retry to promote the service after the user unlocks/changes permission without opening the app.
- Actor/broadcaster lifecycle
- The broadcaster actor may outlive the connection manager/service and could keep jobs alive, causing unexpected logs or work after shutdown.
- Foreground service promotion timing/retries
- Promotion attempts in the periodic loop could miss the 5s window or reinvoke `startForeground()` unnecessarily.
**Recommendations**
- Enforce mesh lifecycle to foreground state
- Start/stop mesh strictly based on foreground eligibility and user preference:
- Only start mesh when both background is enabled and all required permissions are present.
- Stop mesh immediately when background is disabled or notification permission is revoked.
- Where/How:
- Update `ensureMeshStarted()` to require `MeshServicePreferences.isBackgroundEnabled(true)` and `hasNotificationPermission()` in addition to BT perms before starting the mesh. Also add a symmetric `ensureMeshStopped()` when not eligible.
- `app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt:192`
- In the periodic loop, if background is disabled or permissions are missing, also stop the mesh, not just the foreground/notification.
- `app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt:182-191`
- Fix STOP and onDestroy behavior
- STOP action should stop the mesh and clear the holder.
- `onDestroy()` should besteffort stop mesh if this service is the owner.
- Where/How:
- `app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt:133` — call `meshService?.stopServices()` and `MeshServiceHolder.clear()` before `stopSelf()`.
- `app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt:234` — on destroy, if foreground eligibility is off, call `meshService?.stopServices()`; consider a guard not to kill if another visible Activity is using it (see next).
- Introduce simple ownership/reference accounting for `MeshServiceHolder`
- Avoid ambiguous ownership between Activity and FGS:
- Track “UI attached” vs “FGS attached”; only stop underlying mesh when both detach, or when policy requires it (user disabled background).
- Where/How:
- Extend `MeshServiceHolder` with simple ref counts or flags to indicate active owners; add `attach(type)`/`detach(type)`.
- `app/src/main/java/com/bitchat/android/service/MeshServiceHolder.kt`
- In `MainActivity.onStart/onStop` attach/detach the UI owner; in `MeshForegroundService.onCreate/onDestroy` attach/detach the service owner.
- Use a single, parent scope to avoid leaks
- Make `BluetoothPacketBroadcaster` use the injected connection scope instead of creating its own `SupervisorJob()`.
- Where/How:
- `app/src/main/java/com/bitchat/android/mesh/BluetoothPacketBroadcaster.kt:98` — replace `broadcasterScope` with the passed `connectionScope` and remove the extra job. Also ensure any actors/jobs are cancelled when `BluetoothConnectionManager.stopServices()` cancels its scope.
- Tighten foreground promotion logic
- Avoid repeated `startForeground()` calls in the update loop. Promote once on start when eligible; afterwards only call `NotificationManagerCompat.notify()`.
- Where/How:
- `app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt:176-187` — keep a local `isInForeground` flag; only call `startForeground()` on the transition from notinFG to inFG. Use `notify()` for updates.
- Improve boot/permission retry behavior
- When auto-start is enabled but notification permission is missing:
- Consider scheduling a WorkManager task that reevaluates eligibility on unlock or app launch and then promotes the FGS, or rely on an inapp signal sent by permission request flows.
- Where/How:
- `app/src/main/java/com/bitchat/android/service/BootCompletedReceiver.kt:10-19`
- Ensure the onboarding/permission flow emits `ACTION_NOTIFICATION_PERMISSION_GRANTED` broadcast upon grant so `NotificationPermissionChangedReceiver` can promote immediately.
- Default background OFF on fresh installs
- Reduce surprises and align with Play policy expectations by requiring explicit optin to run in background.
- Where/How:
- Change defaults in `MeshServicePreferences`:
- `app/src/main/java/com/bitchat/android/service/MeshServicePreferences.kt:20-32` — set default `false` for `BACKGROUND_ENABLED` and possibly `AUTO_START`.
- Optional: Consolidate service/mesh creation path
- To reduce races and complexity:
- Let the foreground service own `BluetoothMeshService` creation, and have the UI bind to it via the holder.
- Remove the eager start from `BitchatApplication`, and let `MainActivity` only request start of the FGS (which in turn creates/adopts the mesh).
- Where/How:
- `app/src/main/java/com/bitchat/android/BitchatApplication.kt:42-46` — consider removing the proactive start or guard behind explicit background enabled + permission checks.
- Validate read receipt/DM flows without UI
- Current design shows DM notifications via the service when `delegate == null`; thats good.
- Ensure delivery acks and handshake retries dont rely on UI-only paths.
- Where/How:
- Review and, if needed, mirror “UI-triggered” handshake initiation logic inside `BluetoothMeshService` for background-only contexts.
- `app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt:789, 873, 1086-1090`
**Minor Polish**
- Consider adding a user-visible state in the persistent notification when background is disabled or permissions are missing, so the user can tap to enable.
- Where: `app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt:208-240`
- Consider scoping `updateJob` to `serviceJob` and guarding multiple starters.
- Where: `app/src/main/java/com/bitchat/android/service/MeshForegroundService.kt:27, 158-196`
- Revisit scan/advertise settings in Doze with FGS active
- PowerManager duty cycle is good; validate realworld scan rates under Doze while FGS is active and adjust thresholds if relay connectivity drops.
- Where: `app/src/main/java/com/bitchat/android/mesh/PowerManager.kt`
**Suggested Acceptance Criteria After Fixes**
- When background is ON and notifications are granted:
- Mesh starts, persistent notification is visible, scanning/advertising work after swiping the app away.
- When background is OFF or notifications are revoked:
- Foreground service and mesh both stop; no BLE activity remains.
- STOP action fully stops mesh and closes the foreground service.
- After reboot, if background is ON and notification permission is granted:
- Foreground service starts and mesh runs. If not granted, it defers cleanly until the user grants.
- No coroutine/actor leaks after stop/restart cycles:
- Logs cleanly shut down and no lingering “broadcaster actor” messages after stopping.
If you want, I can implement the specific code changes for:
- stopping mesh on STOP/onDestroy,
- gating `ensureMeshStarted()` and adding `ensureMeshStopped()`,
- fixing `BluetoothPacketBroadcaster` to use the injected scope,
- tightening `startForeground()` promotion logic.