Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,23 @@ class BluetoothConnectionManager(
* Public: connect/disconnect helpers for debug UI
*/
fun connectToAddress(address: String): Boolean = clientManager.connectToAddress(address)
fun disconnectAddress(address: String) { connectionTracker.disconnectDevice(address) }
fun disconnectAddress(address: String) {
connectionScope.launch {
try {
val dc = connectionTracker.getDeviceConnection(address)
if (dc != null) {
if (dc.gatt != null) {
try { dc.gatt.disconnect() } catch (_: Exception) { }
} else {
// Try canceling server-side connection if present
try { serverManager.getGattServer()?.cancelConnection(dc.device) } catch (_: Exception) { }
}
}
} catch (_: Exception) { }
// Cleanup tracking regardless
connectionTracker.cleanupDeviceConnection(address)
}
}


// Optionally disconnect all connections (server and client)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,23 @@ class BluetoothConnectionTracker(
fun getSubscribedDevices(): List<BluetoothDevice> {
return subscribedDevices.toList()
}

/**
* Check whether a given peer already has an active direct connection.
*/
fun isPeerDirectlyConnected(peerID: String): Boolean {
val address = getConnectedAddressForPeer(peerID)
return address != null
}

/**
* Return the connected device address for a peer, if currently connected.
*/
fun getConnectedAddressForPeer(peerID: String): String? {
// Find any address mapped to this peer that is still connected
val address = addressPeerMap.entries.firstOrNull { it.value == peerID }?.key
return address?.takeIf { connectedDevices.containsKey(it) }
}

/**
* Get current RSSI for a device address
Expand Down Expand Up @@ -198,7 +215,8 @@ class BluetoothConnectionTracker(
}

// Update connection attempt atomically
val attempts = (currentAttempt?.attempts ?: 0) + 1
// If the previous attempt window expired, reset backoff to 1; otherwise increment
val attempts = if (currentAttempt?.isExpired() == true) 1 else (currentAttempt?.attempts ?: 0) + 1
pendingConnections[deviceAddress] = ConnectionAttempt(attempts)
Log.d(TAG, "Tracker: Added pending connection for $deviceAddress (attempts: $attempts)")
return true
Expand Down Expand Up @@ -283,7 +301,6 @@ class BluetoothConnectionTracker(
subscribedDevices.removeAll { it.address == deviceAddress }
addressPeerMap.remove(deviceAddress)
}
pendingConnections.remove(deviceAddress)
firstAnnounceSeen.remove(deviceAddress)
Log.d(TAG, "Cleaned up device connection for $deviceAddress")
}
Expand Down
28 changes: 19 additions & 9 deletions app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -408,18 +408,28 @@ class BluetoothMeshService(private val context: Context) {
val deviceAddress = routed.relayAddress
val pid = routed.peerID
if (deviceAddress != null && pid != null) {
// First ANNOUNCE over a device connection defines a direct neighbor.
// Only process on first ANNOUNCE seen for this device connection
if (!connectionManager.hasSeenFirstAnnounce(deviceAddress)) {
// Bind or rebind this device address to the announcing peer
connectionManager.addressPeerMap[deviceAddress] = pid
connectionManager.noteAnnounceReceived(deviceAddress)
Log.d(TAG, "Mapped device $deviceAddress to peer $pid on FIRST-ANNOUNCE for this connection")
// If we're already directly connected to this peer via another address, drop the new one.
val existingAddress = connectionManager.addressPeerMap
.entries.firstOrNull { it.value == pid }?.key

// Mark as directly connected (upgrades from routed if needed)
try { peerManager.setDirectConnection(pid, true) } catch (_: Exception) { }
val existingConnected = existingAddress != null &&
connectionManager.isClientConnection(existingAddress!!) != null

// Initial sync for this newly direct peer
try { gossipSyncManager.scheduleInitialSyncToPeer(pid, 1_000) } catch (_: Exception) { }
if (existingAddress != null && existingAddress != deviceAddress && existingConnected) {
Log.i(TAG, "Peer $pid already connected via $existingAddress; dropping new connection $deviceAddress")
// Disconnect the newer duplicate connection
connectionManager.disconnectAddress(deviceAddress)
} else {
// Bind this device address to peer and mark direct
connectionManager.addressPeerMap[deviceAddress] = pid
connectionManager.noteAnnounceReceived(deviceAddress)
Log.d(TAG, "Mapped device $deviceAddress to peer $pid on FIRST-ANNOUNCE for this connection")

try { peerManager.setDirectConnection(pid, true) } catch (_: Exception) { }
try { gossipSyncManager.scheduleInitialSyncToPeer(pid, 1_000) } catch (_: Exception) { }
}
}
}
// Track for sync
Expand Down
Loading