Skip to content

Conversation

@callebtc
Copy link
Collaborator

@callebtc callebtc commented Jan 4, 2026

Description

This PR refactors the mesh networking layer to introduce MeshCore, a shared coordinator that abstracts common mesh logic (peer management, security, packet processing, etc.) away from specific transports. This enables multi-transport support (BLE + Wi-Fi Aware) with shared state and seamless routing between them.

Key Changes:

  • MeshCore Abstraction: Centralized logic for PeerManager, SecurityManager, StoreForwardManager, and PacketProcessor into a reusable MeshCore class.
  • Multi-Transport Support: Both BluetoothMeshService and WifiAwareMeshService now utilize MeshCore to handle protocol-level logic while focusing on their respective hardware transport implementations.
  • Unified Routing: Introduced TransportBridgeService to route packets between different transport layers.
  • Shared Gossip: Both services share a single GossipSyncManager instance via MeshServiceHolder.

Critical Fixes:

  • Memory Leak Fix: MainActivity now properly detaches its WifiAwareMeshDelegate in onPause to prevent leaking the Activity context through the singleton WifiAwareController.
  • Background Persistence & Notifications: Moved message persistence and background DM notifications for Wi-Fi Aware into the service layer via MeshCore.Hooks. This ensures messages are saved to AppStateStore and notifications are shown even when the UI is closed.
  • Race Condition Fix: Guaranteed that BluetoothMeshService is initialized before WifiAwareMeshService to ensure a consistent source for the shared GossipSyncManager.
  • Code Deduplication: Removed redundant persistence logic from MainActivity callbacks.

Checklist

  • I have performed a self-review of my code
  • If it is a core feature, I have added automated tests

* count peers in notification

* cleanup
# Conflicts:
#	app/src/main/res/values-ar/strings.xml
#	app/src/main/res/values-bn/strings.xml
#	app/src/main/res/values-de/strings.xml
#	app/src/main/res/values-es/strings.xml
#	app/src/main/res/values-fa/strings.xml
#	app/src/main/res/values-fil/strings.xml
#	app/src/main/res/values-fr/strings.xml
#	app/src/main/res/values-he/strings.xml
#	app/src/main/res/values-hi/strings.xml
#	app/src/main/res/values-id/strings.xml
#	app/src/main/res/values-it/strings.xml
#	app/src/main/res/values-ja/strings.xml
#	app/src/main/res/values-ka/strings.xml
#	app/src/main/res/values-ko/strings.xml
#	app/src/main/res/values-mg/strings.xml
#	app/src/main/res/values-ms/strings.xml
#	app/src/main/res/values-ne/strings.xml
#	app/src/main/res/values-nl/strings.xml
#	app/src/main/res/values-pa-rPK/strings.xml
#	app/src/main/res/values-pl/strings.xml
#	app/src/main/res/values-pt-rBR/strings.xml
#	app/src/main/res/values-pt/strings.xml
#	app/src/main/res/values-ru/strings.xml
#	app/src/main/res/values-sv/strings.xml
#	app/src/main/res/values-ta/strings.xml
#	app/src/main/res/values-th/strings.xml
#	app/src/main/res/values-tr/strings.xml
#	app/src/main/res/values-uk/strings.xml
#	app/src/main/res/values-ur/strings.xml
#	app/src/main/res/values-vi/strings.xml
#	app/src/main/res/values-zh-rCN/strings.xml
#	app/src/main/res/values-zh-rTW/strings.xml
#	app/src/main/res/values-zh/strings.xml
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5983d700cd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines 94 to 98
if (!isActive) return
isActive = false
announceJob?.cancel()
announceJob = null
gossipSyncManager.stop()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid stopping shared gossip manager from Wi‑Fi

When WifiAwareMeshService is constructed it passes MeshServiceHolder.sharedGossipSyncManager into MeshCore, which is the BLE instance’s gossip manager. stopCore() always calls gossipSyncManager.stop(), so stopping Wi‑Fi will also stop BLE’s gossip sync/cleanup loops even if the BLE service is still running. In practice this means disabling Wi‑Fi Aware (or losing Aware permissions) can silently halt BLE gossip synchronization until BLE is restarted. Consider only starting/stopping the manager when the MeshCore owns it, or add reference counting for shared managers.

Useful? React with 👍 / 👎.

callebtc and others added 4 commits January 4, 2026 22:17
* Refactor WifiAware memory management and lifecycle handling

* Fix Wi-Fi Aware reconnection by ensuring callback unregistration and session watchdog

* fix(wifiaware): allow WifiAwareController to restart service if session drops

* Enhance WiFi Aware logging with network request timeouts and detailed callback status

* Ensure explicit release of WiFi Aware network callbacks on failure or disconnection

---------

Co-authored-by: aidenvalue <>
* fix(wifi-aware): restore peer check and remove aggressive session restarts

* synchronized access to sockets.

* clear old peer socket `onClientConnected` if one was found.

* address second round of review

* fix: Restart publish session on termination in WifiAwareMeshService

* fix: cleanup connection tracker resources when handling peer disconnection without active socket

---------

Co-authored-by: aidenvalu3 <>
Copy link
Collaborator Author

@callebtc callebtc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work on abstracting the Mesh logic! The MeshCore design is much cleaner and allows for easier addition of new transports.

However, I found a few critical issues regarding lifecycle management and data persistence that need to be addressed before merging.

Critical Issues

  1. Memory Leak in MainActivity
    In MainActivity.kt, you set the delegate for WifiAwareMeshService inside a lifecycleScope:

    svc.delegate = object : com.bitchat.android.wifiaware.WifiAwareMeshDelegate { ... }

    However, unlike meshService.delegate = null in onPause(), you never nullify the WifiAwareMeshService delegate. Since WifiAwareController holds a singleton reference to the service, and the anonymous delegate holds a reference to MainActivity (and ChatViewModel), this causes the MainActivity to leak every time it is destroyed/recreated.

  2. Data Persistence & Background Handling
    You are handling message persistence for Wi-Fi Aware inside the MainActivity delegate:

    if (message.isPrivate) { AppStateStore.addPrivateMessage(...) }

    This is risky because:

    • If the UI is not active (app in background), MainActivity might be paused/stopped. If we fix the memory leak by clearing the delegate in onPause, Wi-Fi Aware messages received in the background will be lost (not saved to DB), because WifiAwareMeshService itself doesn't save them.
    • BluetoothMeshService handles this correctly by passing a hook to MeshCore (onMessageReceived = { ... AppStateStore ... }).
    • Fix: Move the AppStateStore persistence logic into WifiAwareMeshService (passing it as a hook to its MeshCore instance), exactly like it is done in BluetoothMeshService. This ensures messages are saved regardless of UI state and removes code duplication from MainActivity.

Architectural Observations

  1. Split Brain Peer State
    You have two MeshCore instances (BLE and Wi-Fi), each with its own PeerManager. The UI (MeshDelegateHandler) merges these lists. This works, but it means BluetoothMeshService doesn't inherently know about Wi-Fi peers (only via the bridge).

    • Make sure TransportBridgeService routing is robust enough that if BluetoothMeshService receives a packet for a Wi-Fi peer, it correctly routes it to the bridge (which it seems to do via dispatchGlobal).
    • Suggestion: Long-term, a single MeshCore managing multiple Transport instances might be cleaner, but the current approach of bridging two services is a valid "micro-service" style approach within the app.
  2. Shared Gossip Manager Race Condition

    val shared = com.bitchat.android.service.MeshServiceHolder.sharedGossipSyncManager
    meshCore = MeshCore(..., sharedGossipManager = shared, ...)

    If WifiAwareMeshService starts before BluetoothMeshService (e.g., race condition in MainActivity init or permission grants), shared will be null. WifiAwareMeshService will create its own. Then BluetoothMeshService starts and creates another one.

    • Fix: Ensure MeshServiceHolder initializes the GossipSyncManager lazily or explicitly before either service starts, so they definitely share the same instance.

Code Style / Minor

  • Duplication: The code in MainActivity to save messages is a copy-paste of logic that should be in the service layer. Moving it to WifiAwareMeshService hooks fixes this.
  • MeshCore Hooks: The Hooks pattern is flexible, but WifiAwareMeshService ignores the onMessageReceived hook, leading to the issue above.

Please address these points, especially the memory leak and the background persistence issue.

…Wi-Fi Aware

- Move message persistence and background notifications for Wi-Fi Aware into the service layer via MeshCore hooks.
- Fix memory leak in MainActivity by properly detaching WifiAwareMeshDelegate on pause.
- Ensure BluetoothMeshService is initialized before WifiAwareMeshService to guarantee shared GossipSyncManager instance.
- Deduplicate persistence logic from MainActivity delegate.
@callebtc callebtc marked this pull request as draft January 10, 2026 15:46
@callebtc callebtc marked this pull request as ready for review January 10, 2026 15:47
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 689f3db1f9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +330 to +336
if (sent) {
// Ensure unread badge is cleared for this peer immediately
try {
val current = state.getUnreadPrivateMessagesValue().toMutableSet()
if (current.remove(senderPeerID)) {
state.setUnreadPrivateMessages(current)
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear unread state regardless of read-receipt send

Because unread badges are cleared only when sent is true, a user who opens a focused chat while the peer is offline/no session will keep seeing the conversation marked unread even though they just read it. The read receipt may legitimately fail to send (no direct connection or session), but local read state should still be cleared to avoid stuck unread indicators until the peer reconnects. Consider clearing the unread set unconditionally and optionally queueing the read receipt separately.

Useful? React with 👍 / 👎.

@callebtc callebtc marked this pull request as draft January 15, 2026 05:53
@callebtc callebtc marked this pull request as ready for review January 15, 2026 05:53
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 689f3db1f9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +74 to +78
if (_running.value) {
if (!_enabled.value) return
// If already marked running but we are calling this (e.g. after a drop),
// ensure we clean up the old service before restarting.
stop()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid restarting Wi‑Fi Aware in startIfPossible

startIfPossible() now stops the service whenever _running is true, so any caller that uses it as an “ensure running” helper will tear down existing Wi‑Fi Aware sessions. Since MainActivity.onResume() now calls WifiAwareController.startIfPossible() when location is enabled, simply backgrounding/foregrounding the app will drop active connections and reset in‑flight transfers. Consider making this path a no‑op when already running (or only restarting after an explicit failure signal) to avoid unnecessary disconnects.

Useful? React with 👍 / 👎.

@alltheseas
Copy link

Critical Issues

  1. Race Condition in Init Order

// WifiAwareMeshService relies on BluetoothMeshService being initialized first
com.bitchat.android.service.MeshServiceHolder.getOrCreate(context)
val shared = MeshServiceHolder.sharedGossipSyncManager // could be null
Risk: If init order changes, Wi-Fi Aware gets its own GossipSyncManager instead of sharing.

  1. Socket Cleanup Unclear

When does peerSockets get cleaned up on disconnect? Need to verify all paths:

  • onLost() callback
  • Peer handle invalidation
  • App backgrounding
  1. No Backpressure on Bridge

TransportBridgeService.broadcast() calls all transports synchronously. If Wi-Fi is slow, BLE packets queue unbounded.

  1. Duplicate Processing Risk

Both BLE and Wi-Fi have separate MeshCore instances. Packet arriving via bridge could be processed twice. Need to verify deduplication works across cores.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants