mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 00:25:20 +00:00
precvent duplicate connections
This commit is contained in:
@@ -44,6 +44,17 @@ class BluetoothConnectionManager(private val context: Context, private val myPee
|
|||||||
private val gattConnections = ConcurrentHashMap<BluetoothDevice, BluetoothGatt>() // Track GATT client connections
|
private val gattConnections = ConcurrentHashMap<BluetoothDevice, BluetoothGatt>() // Track GATT client connections
|
||||||
private val peripheralRSSI = ConcurrentHashMap<String, Int>() // Track RSSI by device address during discovery
|
private val peripheralRSSI = ConcurrentHashMap<String, Int>() // Track RSSI by device address during discovery
|
||||||
|
|
||||||
|
// FIXED: Track connection attempts to prevent duplicates
|
||||||
|
private val pendingConnections = ConcurrentHashMap<String, Long>() // device address -> attempt timestamp
|
||||||
|
private val connectionAttempts = ConcurrentHashMap<String, Int>() // device address -> attempt count
|
||||||
|
private val maxConnectionAttempts = 3
|
||||||
|
private val connectionRetryDelay = 5000L // 5 seconds
|
||||||
|
|
||||||
|
// Service registration tracking
|
||||||
|
private var serviceRegistered = false
|
||||||
|
private var advertisingActive = false
|
||||||
|
private var scanningActive = false
|
||||||
|
|
||||||
// Delegate for callbacks
|
// Delegate for callbacks
|
||||||
var delegate: BluetoothConnectionManagerDelegate? = null
|
var delegate: BluetoothConnectionManagerDelegate? = null
|
||||||
|
|
||||||
@@ -194,6 +205,9 @@ class BluetoothConnectionManager(private val context: Context, private val myPee
|
|||||||
appendLine("BLE Scanner Available: ${bleScanner != null}")
|
appendLine("BLE Scanner Available: ${bleScanner != null}")
|
||||||
appendLine("BLE Advertiser Available: ${bleAdvertiser != null}")
|
appendLine("BLE Advertiser Available: ${bleAdvertiser != null}")
|
||||||
appendLine("GATT Server Active: ${gattServer != null}")
|
appendLine("GATT Server Active: ${gattServer != null}")
|
||||||
|
appendLine("Service Registered: $serviceRegistered")
|
||||||
|
appendLine("Advertising Active: $advertisingActive")
|
||||||
|
appendLine("Scanning Active: $scanningActive")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("Connected Devices: ${connectedDevices.size}")
|
appendLine("Connected Devices: ${connectedDevices.size}")
|
||||||
connectedDevices.forEach { (address, device) ->
|
connectedDevices.forEach { (address, device) ->
|
||||||
@@ -210,6 +224,17 @@ class BluetoothConnectionManager(private val context: Context, private val myPee
|
|||||||
appendLine(" - ${device.address}")
|
appendLine(" - ${device.address}")
|
||||||
}
|
}
|
||||||
appendLine()
|
appendLine()
|
||||||
|
appendLine("Pending Connections: ${pendingConnections.size}")
|
||||||
|
pendingConnections.forEach { (address, timestamp) ->
|
||||||
|
val elapsed = (System.currentTimeMillis() - timestamp) / 1000
|
||||||
|
appendLine(" - $address (${elapsed}s ago)")
|
||||||
|
}
|
||||||
|
appendLine()
|
||||||
|
appendLine("Connection Attempts: ${connectionAttempts.size}")
|
||||||
|
connectionAttempts.forEach { (address, attempts) ->
|
||||||
|
appendLine(" - $address: $attempts attempts")
|
||||||
|
}
|
||||||
|
appendLine()
|
||||||
appendLine("Peripheral RSSI: ${peripheralRSSI.size}")
|
appendLine("Peripheral RSSI: ${peripheralRSSI.size}")
|
||||||
peripheralRSSI.forEach { (address, rssi) ->
|
peripheralRSSI.forEach { (address, rssi) ->
|
||||||
appendLine(" - $address: $rssi dBm")
|
appendLine(" - $address: $rssi dBm")
|
||||||
@@ -514,94 +539,205 @@ class BluetoothConnectionManager(private val context: Context, private val myPee
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle scan result and connect to discovered devices
|
* Handle scan result and connect to discovered devices - FIXED to prevent duplicate connections
|
||||||
*/
|
*/
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
private fun handleScanResult(result: ScanResult) {
|
private fun handleScanResult(result: ScanResult) {
|
||||||
val device = result.device
|
val device = result.device
|
||||||
val rssi = result.rssi
|
val rssi = result.rssi
|
||||||
|
val deviceAddress = device.address
|
||||||
|
|
||||||
// Filter out weak signals
|
// Filter out weak signals
|
||||||
if (rssi < -90) {
|
if (rssi < -90) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if already connected
|
// FIXED: Check for existing client connections only, allow server connections
|
||||||
if (connectedDevices.values.any { it.address == device.address }) {
|
if (gattConnections.containsKey(device)) {
|
||||||
|
Log.d(TAG, "Already have client connection to $deviceAddress")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store RSSI
|
// FIXED: Check for pending connections to prevent duplicates
|
||||||
peripheralRSSI[device.address] = rssi
|
val currentTime = System.currentTimeMillis()
|
||||||
|
val pendingTime = pendingConnections[deviceAddress]
|
||||||
|
if (pendingTime != null && (currentTime - pendingTime) < connectionRetryDelay) {
|
||||||
|
Log.d(TAG, "Connection attempt to $deviceAddress already pending, skipping")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
Log.i(TAG, "Found bitchat service at ${device.address} (RSSI: $rssi), connecting...")
|
// Check connection attempt limits
|
||||||
|
val attempts = connectionAttempts.getOrDefault(deviceAddress, 0)
|
||||||
|
if (attempts >= maxConnectionAttempts) {
|
||||||
|
if (pendingTime == null || (currentTime - pendingTime) > connectionRetryDelay * 2) {
|
||||||
|
// Reset after extended delay
|
||||||
|
connectionAttempts[deviceAddress] = 0
|
||||||
|
pendingConnections.remove(deviceAddress)
|
||||||
|
Log.d(TAG, "Reset connection attempts for $deviceAddress after extended delay")
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Max connection attempts reached for $deviceAddress")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store RSSI and track connection attempt
|
||||||
|
peripheralRSSI[deviceAddress] = rssi
|
||||||
|
pendingConnections[deviceAddress] = currentTime
|
||||||
|
connectionAttempts[deviceAddress] = attempts + 1
|
||||||
|
|
||||||
|
Log.i(TAG, "Found bitchat service at $deviceAddress (RSSI: $rssi), connecting (attempt ${attempts + 1})...")
|
||||||
|
|
||||||
// Connect to device
|
// Connect to device
|
||||||
connectToDevice(device)
|
connectToDeviceFixed(device)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect to a discovered device
|
* Connect to a discovered device - FIXED version with proper timeout and cleanup handling
|
||||||
*/
|
*/
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
private fun connectToDevice(device: BluetoothDevice) {
|
private fun connectToDeviceFixed(device: BluetoothDevice) {
|
||||||
if (!hasBluetoothPermissions()) return
|
if (!hasBluetoothPermissions()) return
|
||||||
|
|
||||||
|
val deviceAddress = device.address
|
||||||
|
Log.d(TAG, "Attempting to connect to $deviceAddress")
|
||||||
|
|
||||||
val gattCallback = object : BluetoothGattCallback() {
|
val gattCallback = object : BluetoothGattCallback() {
|
||||||
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
|
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
|
||||||
|
val statusMessage = when (status) {
|
||||||
|
BluetoothGatt.GATT_SUCCESS -> "SUCCESS"
|
||||||
|
147 -> "CONNECTION_TIMEOUT"
|
||||||
|
133 -> "GATT_ERROR"
|
||||||
|
8 -> "CONNECTION_TIMEOUT"
|
||||||
|
else -> "ERROR_$status"
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "onConnectionStateChange: $deviceAddress status=$statusMessage($status) newState=$newState")
|
||||||
|
|
||||||
when (newState) {
|
when (newState) {
|
||||||
BluetoothProfile.STATE_CONNECTED -> {
|
BluetoothProfile.STATE_CONNECTED -> {
|
||||||
Log.d(TAG, "Connected to ${gatt.device.address} as client")
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||||
connectedDevices[gatt.device.address] = gatt.device
|
Log.i(TAG, "Successfully connected to $deviceAddress as client")
|
||||||
gattConnections[gatt.device] = gatt
|
connectedDevices[deviceAddress] = gatt.device
|
||||||
gatt.discoverServices()
|
gattConnections[gatt.device] = gatt
|
||||||
|
pendingConnections.remove(deviceAddress) // Clear pending connection
|
||||||
|
|
||||||
|
// Discover services with a slight delay for stability
|
||||||
|
connectionScope.launch {
|
||||||
|
delay(200) // Small delay for connection stabilization
|
||||||
|
if (gattConnections.containsKey(gatt.device)) {
|
||||||
|
gatt.discoverServices()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Connected to $deviceAddress with error status: $status")
|
||||||
|
gatt.disconnect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BluetoothProfile.STATE_DISCONNECTED -> {
|
BluetoothProfile.STATE_DISCONNECTED -> {
|
||||||
Log.d(TAG, "Disconnected from ${gatt.device.address}")
|
Log.d(TAG, "Disconnected from $deviceAddress (status: $status)")
|
||||||
connectedDevices.remove(gatt.device.address)
|
connectedDevices.remove(deviceAddress)
|
||||||
deviceCharacteristics.remove(gatt.device)
|
deviceCharacteristics.remove(gatt.device)
|
||||||
gattConnections.remove(gatt.device)
|
gattConnections.remove(gatt.device)
|
||||||
gatt.close()
|
pendingConnections.remove(deviceAddress) // Clear pending connection
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
|
|
||||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
|
||||||
val service = gatt.getService(SERVICE_UUID)
|
|
||||||
val characteristic = service?.getCharacteristic(CHARACTERISTIC_UUID)
|
|
||||||
|
|
||||||
if (characteristic != null) {
|
|
||||||
deviceCharacteristics[gatt.device] = characteristic
|
|
||||||
gatt.setCharacteristicNotification(characteristic, true)
|
|
||||||
|
|
||||||
// Enable notifications
|
// Handle connection errors
|
||||||
val descriptor = characteristic.getDescriptor(
|
if (status == 147 || status == 133 || status == 8) { // Timeout or connection errors
|
||||||
UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
|
Log.w(TAG, "Connection failed to $deviceAddress with status $status")
|
||||||
)
|
}
|
||||||
descriptor?.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
|
|
||||||
gatt.writeDescriptor(descriptor)
|
|
||||||
|
|
||||||
// Notify delegate
|
// Close the GATT connection
|
||||||
connectionScope.launch {
|
try {
|
||||||
delay(200)
|
gatt.close()
|
||||||
delegate?.onDeviceConnected(gatt.device)
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error closing GATT connection to $deviceAddress: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
|
||||||
|
Log.d(TAG, "onServicesDiscovered: $deviceAddress status=$status")
|
||||||
|
|
||||||
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||||
|
val service = gatt.getService(SERVICE_UUID)
|
||||||
|
val characteristic = service?.getCharacteristic(CHARACTERISTIC_UUID)
|
||||||
|
|
||||||
|
if (characteristic != null) {
|
||||||
|
Log.d(TAG, "Found bitchat service and characteristic on $deviceAddress")
|
||||||
|
deviceCharacteristics[gatt.device] = characteristic
|
||||||
|
|
||||||
|
// Enable notifications
|
||||||
|
val notificationSuccess = gatt.setCharacteristicNotification(characteristic, true)
|
||||||
|
Log.d(TAG, "Set notification for $deviceAddress: $notificationSuccess")
|
||||||
|
|
||||||
|
// Write notification descriptor
|
||||||
|
val descriptor = characteristic.getDescriptor(
|
||||||
|
UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
|
||||||
|
)
|
||||||
|
if (descriptor != null) {
|
||||||
|
descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
|
||||||
|
val descriptorSuccess = gatt.writeDescriptor(descriptor)
|
||||||
|
Log.d(TAG, "Write notification descriptor for $deviceAddress: $descriptorSuccess")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify delegate about successful connection
|
||||||
|
connectionScope.launch {
|
||||||
|
delay(100) // Ensure everything is set up
|
||||||
|
delegate?.onDeviceConnected(gatt.device)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Bitchat service or characteristic not found on $deviceAddress")
|
||||||
|
gatt.disconnect()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Service discovery failed on $deviceAddress with status: $status")
|
||||||
|
gatt.disconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
|
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
|
||||||
val value = characteristic.value
|
val value = characteristic.value
|
||||||
|
Log.d(TAG, "Received ${value.size} bytes from $deviceAddress")
|
||||||
|
|
||||||
val packet = BitchatPacket.fromBinaryData(value)
|
val packet = BitchatPacket.fromBinaryData(value)
|
||||||
if (packet != null) {
|
if (packet != null) {
|
||||||
val peerID = String(packet.senderID).replace("\u0000", "")
|
val peerID = String(packet.senderID).replace("\u0000", "")
|
||||||
delegate?.onPacketReceived(packet, peerID, gatt.device)
|
delegate?.onPacketReceived(packet, peerID, gatt.device)
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Failed to parse packet from $deviceAddress")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
|
||||||
|
Log.d(TAG, "onDescriptorWrite: $deviceAddress status=$status")
|
||||||
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||||
|
Log.i(TAG, "Successfully enabled notifications for $deviceAddress")
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Failed to enable notifications for $deviceAddress: $status")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
device.connectGatt(context, false, gattCallback)
|
// Attempt the connection
|
||||||
|
try {
|
||||||
|
val gatt = device.connectGatt(context, false, gattCallback)
|
||||||
|
if (gatt == null) {
|
||||||
|
Log.e(TAG, "Failed to create GATT connection to $deviceAddress")
|
||||||
|
pendingConnections.remove(deviceAddress)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Exception connecting to $deviceAddress: ${e.message}")
|
||||||
|
pendingConnections.remove(deviceAddress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to a discovered device - LEGACY METHOD (kept for compatibility)
|
||||||
|
*/
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
private fun connectToDevice(device: BluetoothDevice) {
|
||||||
|
// Redirect to fixed method
|
||||||
|
connectToDeviceFixed(device)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -613,6 +749,9 @@ class BluetoothConnectionManager(private val context: Context, private val myPee
|
|||||||
subscribedDevices.clear()
|
subscribedDevices.clear()
|
||||||
gattConnections.clear()
|
gattConnections.clear()
|
||||||
peripheralRSSI.clear()
|
peripheralRSSI.clear()
|
||||||
|
// FIXED: Clear new tracking maps
|
||||||
|
pendingConnections.clear()
|
||||||
|
connectionAttempts.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user