From ddba8412803c84e9e35a68fd75ec455a361a2cc5 Mon Sep 17 00:00:00 2001 From: aidenvalue <> Date: Sat, 3 Jan 2026 15:03:30 +0100 Subject: [PATCH] Share GossipSyncManager across transports to prevent redundant message synchronization - Registered BluetoothMeshService's GossipSyncManager as a singleton in MeshServiceHolder. - Modified WifiAwareMeshService to use the shared GossipSyncManager if available. - Added background cleanup for peer mappings on socket disconnection. - Fixed Kotlin type mismatch during nickname map merging. --- .../android/mesh/BluetoothMeshService.kt | 3 ++ .../android/service/MeshServiceHolder.kt | 6 +++ .../com/bitchat/android/ui/ChatViewModel.kt | 4 +- .../wifi-aware/WifiAwareMeshService.kt | 54 +++++++++++-------- 4 files changed, 44 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt b/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt index 6e31932a..c48ce087 100644 --- a/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt +++ b/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt @@ -94,6 +94,9 @@ class BluetoothMeshService(private val context: Context) { } catch (_: Exception) { 0.01 } } ) + + // Register as shared instance for Wi-Fi Aware transport + com.bitchat.android.service.MeshServiceHolder.setGossipManager(gossipSyncManager) // Wire sync manager delegate gossipSyncManager.delegate = object : GossipSyncManager.Delegate { diff --git a/app/src/main/java/com/bitchat/android/service/MeshServiceHolder.kt b/app/src/main/java/com/bitchat/android/service/MeshServiceHolder.kt index d271ab29..71dddb66 100644 --- a/app/src/main/java/com/bitchat/android/service/MeshServiceHolder.kt +++ b/app/src/main/java/com/bitchat/android/service/MeshServiceHolder.kt @@ -9,6 +9,12 @@ import com.bitchat.android.mesh.BluetoothMeshService */ object MeshServiceHolder { private const val TAG = "MeshServiceHolder" + @Volatile + var sharedGossipSyncManager: com.bitchat.android.sync.GossipSyncManager? = null + private set + + fun setGossipManager(mgr: com.bitchat.android.sync.GossipSyncManager) { sharedGossipSyncManager = mgr } + @Volatile var meshService: BluetoothMeshService? = null private set diff --git a/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt b/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt index b516eb26..22345923 100644 --- a/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt +++ b/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt @@ -654,8 +654,8 @@ class ChatViewModel( // Merge nicknames from BLE and Wi‑Fi Aware to display names for all peers val bleNick = meshService.getPeerNicknames() - val awareNick = try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.getPeerNicknamesMap() } catch (_: Exception) { null } - val mergedNick = if (awareNick != null) bleNick + awareNick.filterKeys { it !in bleNick || bleNick[it].isNullOrBlank() } else bleNick + val awareNickRaw = try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.getPeerNicknamesMap() } catch (_: Exception) { null } + val mergedNick = if (awareNickRaw != null) bleNick + awareNickRaw.filter { it.value != null }.mapValues { it.value!! }.filterKeys { it !in bleNick || bleNick[it].isNullOrBlank() } else bleNick state.setPeerNicknames(mergedNick) val rssiValues = meshService.getPeerRSSI() diff --git a/app/src/main/java/com/bitchat/android/wifi-aware/WifiAwareMeshService.kt b/app/src/main/java/com/bitchat/android/wifi-aware/WifiAwareMeshService.kt index b507a3e9..8a07364a 100644 --- a/app/src/main/java/com/bitchat/android/wifi-aware/WifiAwareMeshService.kt +++ b/app/src/main/java/com/bitchat/android/wifi-aware/WifiAwareMeshService.kt @@ -106,24 +106,30 @@ class WifiAwareMeshService(private val context: Context) { setupDelegates() messageHandler.packetProcessor = packetProcessor - gossipSyncManager = GossipSyncManager( - myPeerID = myPeerID, - scope = serviceScope, - configProvider = object : GossipSyncManager.ConfigProvider { - override fun seenCapacity(): Int = 500 - override fun gcsMaxBytes(): Int = 400 - override fun gcsTargetFpr(): Double = 0.01 - } - ) - gossipSyncManager.delegate = object : GossipSyncManager.Delegate { - override fun sendPacketToPeer(peerID: String, packet: BitchatPacket) { - this@WifiAwareMeshService.sendPacketToPeer(peerID, packet) - } - override fun sendPacket(packet: BitchatPacket) { - broadcastPacket(RoutedPacket(packet)) - } - override fun signPacketForBroadcast(packet: BitchatPacket): BitchatPacket { - return signPacketBeforeBroadcast(packet) + // Use shared GossipSyncManager from MeshServiceHolder if available (minimal refactor) + val shared = com.bitchat.android.service.MeshServiceHolder.sharedGossipSyncManager + if (shared != null) { + gossipSyncManager = shared + } else { + gossipSyncManager = GossipSyncManager( + myPeerID = myPeerID, + scope = serviceScope, + configProvider = object : GossipSyncManager.ConfigProvider { + override fun seenCapacity(): Int = 500 + override fun gcsMaxBytes(): Int = 400 + override fun gcsTargetFpr(): Double = 0.01 + } + ) + gossipSyncManager.delegate = object : GossipSyncManager.Delegate { + override fun sendPacketToPeer(peerID: String, packet: BitchatPacket) { + this@WifiAwareMeshService.sendPacketToPeer(peerID, packet) + } + override fun sendPacket(packet: BitchatPacket) { + broadcastPacket(RoutedPacket(packet)) + } + override fun signPacketForBroadcast(packet: BitchatPacket): BitchatPacket { + return signPacketBeforeBroadcast(packet) + } } } } @@ -619,7 +625,7 @@ class WifiAwareMeshService(private val context: Context) { } override fun onLost(network: Network) { networkCallbacks.remove(peerId) - Log.d(TAG, "SERVER: network lost for ${peerId.take(8)}…") + Log.d(TAG, "SERVER: network lost for \${peerId.take(8)}…") } } @@ -826,15 +832,21 @@ class WifiAwareMeshService(private val context: Context) { } if (routedPeerId == null) { + routedPeerId = senderPeerHex + peerSockets[routedPeerId] = socket + } + Log.d(TAG, "RX: packet type=${pkt.type} from ${senderPeerHex.take(8)}… (bytes=${raw.size})") packetProcessor.processPacket(RoutedPacket(pkt, routedPeerId)) } - - // Loop break -> disconnection + handlePeerDisconnection(initialLogicalPeerId, routedPeerId) socket.closeQuietly() } + /** + * Internal helper to ensure peer is cleaned up from all managers when a socket dies. + */ private fun handlePeerDisconnection(initialId: String, routedId: String?) { serviceScope.launch { Log.w(TAG, "Cleaning up peer: $initialId / $routedId")