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.
This commit is contained in:
aidenvalue
2026-01-03 15:03:30 +01:00
parent ae87530afb
commit ddba841280
4 changed files with 44 additions and 23 deletions
@@ -95,6 +95,9 @@ class BluetoothMeshService(private val context: Context) {
} }
) )
// Register as shared instance for Wi-Fi Aware transport
com.bitchat.android.service.MeshServiceHolder.setGossipManager(gossipSyncManager)
// Wire sync manager delegate // Wire sync manager delegate
gossipSyncManager.delegate = object : GossipSyncManager.Delegate { gossipSyncManager.delegate = object : GossipSyncManager.Delegate {
override fun sendPacket(packet: BitchatPacket) { override fun sendPacket(packet: BitchatPacket) {
@@ -9,6 +9,12 @@ import com.bitchat.android.mesh.BluetoothMeshService
*/ */
object MeshServiceHolder { object MeshServiceHolder {
private const val TAG = "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 @Volatile
var meshService: BluetoothMeshService? = null var meshService: BluetoothMeshService? = null
private set private set
@@ -654,8 +654,8 @@ class ChatViewModel(
// Merge nicknames from BLE and WiFi Aware to display names for all peers // Merge nicknames from BLE and WiFi Aware to display names for all peers
val bleNick = meshService.getPeerNicknames() val bleNick = meshService.getPeerNicknames()
val awareNick = try { com.bitchat.android.wifiaware.WifiAwareController.getService()?.getPeerNicknamesMap() } catch (_: Exception) { null } val awareNickRaw = 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 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) state.setPeerNicknames(mergedNick)
val rssiValues = meshService.getPeerRSSI() val rssiValues = meshService.getPeerRSSI()
@@ -106,24 +106,30 @@ class WifiAwareMeshService(private val context: Context) {
setupDelegates() setupDelegates()
messageHandler.packetProcessor = packetProcessor messageHandler.packetProcessor = packetProcessor
gossipSyncManager = GossipSyncManager( // Use shared GossipSyncManager from MeshServiceHolder if available (minimal refactor)
myPeerID = myPeerID, val shared = com.bitchat.android.service.MeshServiceHolder.sharedGossipSyncManager
scope = serviceScope, if (shared != null) {
configProvider = object : GossipSyncManager.ConfigProvider { gossipSyncManager = shared
override fun seenCapacity(): Int = 500 } else {
override fun gcsMaxBytes(): Int = 400 gossipSyncManager = GossipSyncManager(
override fun gcsTargetFpr(): Double = 0.01 myPeerID = myPeerID,
} scope = serviceScope,
) configProvider = object : GossipSyncManager.ConfigProvider {
gossipSyncManager.delegate = object : GossipSyncManager.Delegate { override fun seenCapacity(): Int = 500
override fun sendPacketToPeer(peerID: String, packet: BitchatPacket) { override fun gcsMaxBytes(): Int = 400
this@WifiAwareMeshService.sendPacketToPeer(peerID, packet) override fun gcsTargetFpr(): Double = 0.01
} }
override fun sendPacket(packet: BitchatPacket) { )
broadcastPacket(RoutedPacket(packet)) gossipSyncManager.delegate = object : GossipSyncManager.Delegate {
} override fun sendPacketToPeer(peerID: String, packet: BitchatPacket) {
override fun signPacketForBroadcast(packet: BitchatPacket): BitchatPacket { this@WifiAwareMeshService.sendPacketToPeer(peerID, packet)
return signPacketBeforeBroadcast(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) { override fun onLost(network: Network) {
networkCallbacks.remove(peerId) 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) { if (routedPeerId == null) {
routedPeerId = senderPeerHex
peerSockets[routedPeerId] = socket
}
Log.d(TAG, "RX: packet type=${pkt.type} from ${senderPeerHex.take(8)}… (bytes=${raw.size})") Log.d(TAG, "RX: packet type=${pkt.type} from ${senderPeerHex.take(8)}… (bytes=${raw.size})")
packetProcessor.processPacket(RoutedPacket(pkt, routedPeerId)) packetProcessor.processPacket(RoutedPacket(pkt, routedPeerId))
} }
// Loop break -> disconnection
handlePeerDisconnection(initialLogicalPeerId, routedPeerId) handlePeerDisconnection(initialLogicalPeerId, routedPeerId)
socket.closeQuietly() socket.closeQuietly()
} }
/**
* Internal helper to ensure peer is cleaned up from all managers when a socket dies.
*/
private fun handlePeerDisconnection(initialId: String, routedId: String?) { private fun handlePeerDisconnection(initialId: String, routedId: String?) {
serviceScope.launch { serviceScope.launch {
Log.w(TAG, "Cleaning up peer: $initialId / $routedId") Log.w(TAG, "Cleaning up peer: $initialId / $routedId")