Merge branch 'fix/ble-limits' into gossip-routing-tmp-with-connection-limit-fixed

This commit is contained in:
callebtc
2026-01-09 20:14:16 +07:00
4 changed files with 97 additions and 22 deletions
+5
View File
@@ -26,3 +26,8 @@
-keepnames class org.torproject.arti.**
-dontwarn info.guardianproject.arti.**
-dontwarn org.torproject.arti.**
# Fix for AbstractMethodError on API < 29 where LocationListener methods are abstract
-keepclassmembers class * implements android.location.LocationListener {
public <methods>;
}
@@ -109,11 +109,19 @@ class BluetoothConnectionManager(
}
// Connection caps: enforce on change
connectionScope.launch {
dbg.maxConnectionsOverall.collect {
dbg.maxConnectionsOverall.collect { maxOverall ->
if (!isActive) return@collect
// 1. Enforce client limits (handled by tracker)
connectionTracker.enforceConnectionLimits()
// Also enforce server side best-effort
serverManager.enforceServerLimit(dbg.maxServerConnections.value)
// 2. Enforce overall limit on server connections if needed
// (Tracker knows about all connections but can't disconnect servers directly)
val maxServer = dbg.maxServerConnections.value
val excessServers = connectionTracker.getExcessServerConnections(maxServer, maxOverall)
excessServers.forEach { device ->
Log.d(TAG, "Disconnecting server ${device.address} due to overall cap")
serverManager.disconnectDevice(device)
}
}
}
connectionScope.launch {
@@ -123,9 +131,18 @@ class BluetoothConnectionManager(
}
}
connectionScope.launch {
dbg.maxServerConnections.collect {
dbg.maxServerConnections.collect { maxServer ->
if (!isActive) return@collect
serverManager.enforceServerLimit(dbg.maxServerConnections.value)
// Enforce server specific limit
serverManager.enforceServerLimit(maxServer)
// Also check if this change puts us over the overall limit
val maxOverall = dbg.maxConnectionsOverall.value
val excessServers = connectionTracker.getExcessServerConnections(maxServer, maxOverall)
excessServers.forEach { device ->
Log.d(TAG, "Disconnecting server ${device.address} due to overall cap")
serverManager.disconnectDevice(device)
}
}
}
} catch (_: Exception) { }
@@ -229,11 +229,17 @@ class BluetoothConnectionTracker(
*/
fun getConnectedDeviceCount(): Int = connectedDevices.size
/**
* Check if connection limit is reached
*/
/**
* Check if connection limit is reached
*/
fun isConnectionLimitReached(): Boolean {
return connectedDevices.size >= powerManager.getMaxConnections()
// Respect debug override if set
val dbg = try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance() } catch (_: Exception) { null }
val maxConnections = dbg?.maxConnectionsOverall?.value ?: powerManager.getMaxConnections()
return connectedDevices.size >= maxConnections
}
/**
@@ -244,10 +250,9 @@ class BluetoothConnectionTracker(
val dbg = try { com.bitchat.android.ui.debug.DebugSettingsManager.getInstance() } catch (_: Exception) { null }
val maxOverall = dbg?.maxConnectionsOverall?.value ?: powerManager.getMaxConnections()
val maxClient = dbg?.maxClientConnections?.value ?: maxOverall
val maxServer = dbg?.maxServerConnections?.value ?: maxOverall
// Note: maxServer is handled by GattServerManager, but we need to respect overall limit here too
val clients = connectedDevices.values.filter { it.isClient }
val servers = connectedDevices.values.filter { !it.isClient }
// Enforce client cap first (we can actively disconnect)
if (clients.size > maxClient) {
@@ -259,22 +264,56 @@ class BluetoothConnectionTracker(
}
}
// Note: server cap enforced in GattServerManager (we don't have server handle here)
// Enforce overall cap by disconnecting oldest client connections
// Re-check overall cap after client cleanup
if (connectedDevices.size > maxOverall) {
Log.i(TAG, "Enforcing overall cap: ${connectedDevices.size} > $maxOverall")
val excess = connectedDevices.size - maxOverall
val toDisconnect = connectedDevices.values
.filter { it.isClient } // only clients from here
// Prefer disconnecting clients first to satisfy overall cap
val clientsToDisconnect = connectedDevices.values
.filter { it.isClient }
.sortedBy { it.connectedAt }
.take(excess)
toDisconnect.forEach { dc ->
clientsToDisconnect.forEach { dc ->
Log.d(TAG, "Disconnecting client ${dc.device.address} due to overall cap")
dc.gatt?.disconnect()
}
}
}
/**
* Get excess server connections that should be disconnected to satisfy limits.
* This allows the Manager to coordinate server disconnects since Tracker doesn't control the server.
*/
fun getExcessServerConnections(maxServer: Int, maxOverall: Int): List<BluetoothDevice> {
val servers = connectedDevices.values.filter { !it.isClient }
val excessList = mutableListOf<BluetoothDevice>()
// 1. Check server specific limit
if (servers.size > maxServer) {
val excessCount = servers.size - maxServer
val toRemove = servers.sortedBy { it.connectedAt }.take(excessCount)
excessList.addAll(toRemove.map { it.device })
}
// 2. Check overall limit (considering we might have already removed some above)
// We need to count how many connections we will have after the above removals
val currentTotal = connectedDevices.size
val plannedRemovals = excessList.size
val projectedTotal = currentTotal - plannedRemovals
if (projectedTotal > maxOverall) {
val furtherExcess = projectedTotal - maxOverall
// We can only remove servers here. Clients are handled in enforceConnectionLimits.
// Filter out devices we already planned to remove
val remainingServers = servers.filter { s -> excessList.none { it.address == s.device.address } }
val toRemove = remainingServers.sortedBy { it.connectedAt }.take(furtherExcess)
excessList.addAll(toRemove.map { it.device })
}
return excessList
}
/**
* Clean up a specific device connection
@@ -49,15 +49,28 @@ class BluetoothGattServerManager(
fun enforceServerLimit(maxServer: Int) {
if (maxServer <= 0) return
try {
val subs = connectionTracker.getSubscribedDevices()
if (subs.size > maxServer) {
val excess = subs.size - maxServer
subs.take(excess).forEach { d ->
try { gattServer?.cancelConnection(d) } catch (_: Exception) { }
// Use connection tracker to get actual connected server devices
val servers = connectionTracker.getConnectedDevices().values.filter { !it.isClient }
if (servers.size > maxServer) {
val excess = servers.size - maxServer
// Disconnect oldest
servers.sortedBy { it.connectedAt }.take(excess).forEach { d ->
try { gattServer?.cancelConnection(d.device) } catch (_: Exception) { }
}
}
} catch (_: Exception) { }
}
/**
* Disconnect a specific device (used by ConnectionManager to enforce overall limits)
*/
fun disconnectDevice(device: BluetoothDevice) {
try {
gattServer?.cancelConnection(device)
} catch (e: Exception) {
Log.w(TAG, "Error disconnecting device ${device.address}: ${e.message}")
}
}
/**
* Start GATT server
@@ -122,9 +135,10 @@ class BluetoothGattServerManager(
// Try to cancel any active connections explicitly before closing
try {
val devices = connectionTracker.getSubscribedDevices()
devices.forEach { d ->
try { gattServer?.cancelConnection(d) } catch (_: Exception) { }
// Disconnect ALL server connections
val servers = connectionTracker.getConnectedDevices().values.filter { !it.isClient }
servers.forEach { d ->
try { gattServer?.cancelConnection(d.device) } catch (_: Exception) { }
}
} catch (_: Exception) { }