mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 16:05:20 +00:00
announce peer ID in servicedata to prevent duplicate connections (#613)
This commit is contained in:
@@ -70,7 +70,7 @@ class BluetoothConnectionManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val serverManager = BluetoothGattServerManager(
|
private val serverManager = BluetoothGattServerManager(
|
||||||
context, connectionScope, connectionTracker, permissionManager, powerManager, componentDelegate
|
context, connectionScope, connectionTracker, permissionManager, powerManager, componentDelegate, myPeerID
|
||||||
)
|
)
|
||||||
private val clientManager = BluetoothGattClientManager(
|
private val clientManager = BluetoothGattClientManager(
|
||||||
context, connectionScope, connectionTracker, permissionManager, powerManager, componentDelegate
|
context, connectionScope, connectionTracker, permissionManager, powerManager, componentDelegate
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ class BluetoothConnectionTracker(
|
|||||||
val characteristic: BluetoothGattCharacteristic? = null,
|
val characteristic: BluetoothGattCharacteristic? = null,
|
||||||
val rssi: Int = Int.MIN_VALUE,
|
val rssi: Int = Int.MIN_VALUE,
|
||||||
val isClient: Boolean = false,
|
val isClient: Boolean = false,
|
||||||
val connectedAt: Long = System.currentTimeMillis()
|
val connectedAt: Long = System.currentTimeMillis(),
|
||||||
|
val peerID: String? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -171,6 +172,14 @@ class BluetoothConnectionTracker(
|
|||||||
return connectedDevices.containsKey(deviceAddress)
|
return connectedDevices.containsKey(deviceAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a peer is already connected (by PeerID)
|
||||||
|
*/
|
||||||
|
fun isPeerConnected(peerID: String): Boolean {
|
||||||
|
// Only consider actual connected devices that have identified themselves
|
||||||
|
return connectedDevices.values.any { it.peerID == peerID }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if connection attempt is allowed
|
* Check if connection attempt is allowed
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -320,6 +320,22 @@ class BluetoothGattClientManager(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to extract peerID from Service Data (if available) for stable identity
|
||||||
|
val serviceData = scanRecord?.getServiceData(ParcelUuid(AppConstants.Mesh.Gatt.SERVICE_UUID))
|
||||||
|
val peerID = if (serviceData != null && serviceData.size >= 8) {
|
||||||
|
serviceData.joinToString("") { "%02x".format(it) }
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peerID != null) {
|
||||||
|
// Log.v(TAG, "Found peerID $peerID in scan record for $deviceAddress")
|
||||||
|
if (connectionTracker.isPeerConnected(peerID)) {
|
||||||
|
Log.d(TAG, "Deduplication: Peer $peerID is already connected (ignoring $deviceAddress)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Log.d(TAG, "Received scan result from $deviceAddress - already connected: ${connectionTracker.isDeviceConnected(deviceAddress)}")
|
// Log.d(TAG, "Received scan result from $deviceAddress - already connected: ${connectionTracker.isDeviceConnected(deviceAddress)}")
|
||||||
|
|
||||||
// Store RSSI from scan results for later use (especially for server connections)
|
// Store RSSI from scan results for later use (especially for server connections)
|
||||||
@@ -332,7 +348,7 @@ class BluetoothGattClientManager(
|
|||||||
deviceName = device.name,
|
deviceName = device.name,
|
||||||
deviceAddress = deviceAddress,
|
deviceAddress = deviceAddress,
|
||||||
rssi = rssi,
|
rssi = rssi,
|
||||||
peerID = null // peerID unknown at scan time
|
peerID = peerID // Use the discovered peerID if available
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} catch (_: Exception) { }
|
} catch (_: Exception) { }
|
||||||
@@ -342,13 +358,12 @@ class BluetoothGattClientManager(
|
|||||||
Log.d(TAG, "Skipping device $deviceAddress due to weak signal: $rssi < ${powerManager.getRSSIThreshold()}")
|
Log.d(TAG, "Skipping device $deviceAddress due to weak signal: $rssi < ${powerManager.getRSSIThreshold()}")
|
||||||
// Even if we skip connecting, still publish scan result to debug UI
|
// Even if we skip connecting, still publish scan result to debug UI
|
||||||
try {
|
try {
|
||||||
val pid: String? = null // We don't know peerID until packet exchange
|
|
||||||
DebugSettingsManager.getInstance().addScanResult(
|
DebugSettingsManager.getInstance().addScanResult(
|
||||||
DebugScanResult(
|
DebugScanResult(
|
||||||
deviceName = device.name,
|
deviceName = device.name,
|
||||||
deviceAddress = deviceAddress,
|
deviceAddress = deviceAddress,
|
||||||
rssi = rssi,
|
rssi = rssi,
|
||||||
peerID = pid
|
peerID = peerID
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} catch (_: Exception) { }
|
} catch (_: Exception) { }
|
||||||
@@ -373,7 +388,7 @@ class BluetoothGattClientManager(
|
|||||||
|
|
||||||
// Add pending connection and start connection
|
// Add pending connection and start connection
|
||||||
if (connectionTracker.addPendingConnection(deviceAddress)) {
|
if (connectionTracker.addPendingConnection(deviceAddress)) {
|
||||||
connectToDevice(device, rssi)
|
connectToDevice(device, rssi, peerID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,11 +396,11 @@ class BluetoothGattClientManager(
|
|||||||
* Connect to a device as GATT client
|
* Connect to a device as GATT client
|
||||||
*/
|
*/
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
private fun connectToDevice(device: BluetoothDevice, rssi: Int) {
|
private fun connectToDevice(device: BluetoothDevice, rssi: Int, peerID: String? = null) {
|
||||||
if (!permissionManager.hasBluetoothPermissions()) return
|
if (!permissionManager.hasBluetoothPermissions()) return
|
||||||
|
|
||||||
val deviceAddress = device.address
|
val deviceAddress = device.address
|
||||||
Log.i(TAG, "Connecting to bitchat device: $deviceAddress")
|
Log.i(TAG, "Connecting to bitchat device: $deviceAddress (peerID: $peerID)")
|
||||||
|
|
||||||
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) {
|
||||||
@@ -435,7 +450,8 @@ class BluetoothGattClientManager(
|
|||||||
device = gatt.device,
|
device = gatt.device,
|
||||||
gatt = gatt,
|
gatt = gatt,
|
||||||
rssi = rssi,
|
rssi = rssi,
|
||||||
isClient = true
|
isClient = true,
|
||||||
|
peerID = peerID // Store the peerID discovered during scan
|
||||||
)
|
)
|
||||||
connectionTracker.addDeviceConnection(deviceAddress, deviceConn)
|
connectionTracker.addDeviceConnection(deviceAddress, deviceConn)
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ class BluetoothGattServerManager(
|
|||||||
private val connectionTracker: BluetoothConnectionTracker,
|
private val connectionTracker: BluetoothConnectionTracker,
|
||||||
private val permissionManager: BluetoothPermissionManager,
|
private val permissionManager: BluetoothPermissionManager,
|
||||||
private val powerManager: PowerManager,
|
private val powerManager: PowerManager,
|
||||||
private val delegate: BluetoothConnectionManagerDelegate?
|
private val delegate: BluetoothConnectionManagerDelegate?,
|
||||||
|
private val myPeerID: String
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -373,12 +374,26 @@ class BluetoothGattServerManager(
|
|||||||
.setIncludeDeviceName(false)
|
.setIncludeDeviceName(false)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
// Add stable identity (first 8 bytes of peerID) to Scan Response
|
||||||
|
// This allows scanners to deduplicate devices even if MAC address rotates
|
||||||
|
val peerIDBytes = try {
|
||||||
|
myPeerID.chunked(2).map { it.toInt(16).toByte() }.toByteArray().take(8).toByteArray()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
ByteArray(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
val scanResponse = AdvertiseData.Builder()
|
||||||
|
.addServiceData(ParcelUuid(AppConstants.Mesh.Gatt.SERVICE_UUID), peerIDBytes)
|
||||||
|
.setIncludeTxPowerLevel(false)
|
||||||
|
.setIncludeDeviceName(false)
|
||||||
|
.build()
|
||||||
|
|
||||||
advertiseCallback = object : AdvertiseCallback() {
|
advertiseCallback = object : AdvertiseCallback() {
|
||||||
override fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
|
override fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
|
||||||
val mode = try {
|
val mode = try {
|
||||||
powerManager.getPowerInfo().split("Current Mode: ")[1].split("\n")[0]
|
powerManager.getPowerInfo().split("Current Mode: ")[1].split("\n")[0]
|
||||||
} catch (_: Exception) { "unknown" }
|
} catch (_: Exception) { "unknown" }
|
||||||
Log.i(TAG, "Advertising started (power mode: $mode)")
|
Log.i(TAG, "Advertising started (power mode: $mode) with stable ID: ${peerIDBytes.joinToString("") { "%02x".format(it) }}")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartFailure(errorCode: Int) {
|
override fun onStartFailure(errorCode: Int) {
|
||||||
@@ -387,7 +402,7 @@ class BluetoothGattServerManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bleAdvertiser.startAdvertising(settings, data, advertiseCallback)
|
bleAdvertiser.startAdvertising(settings, data, scanResponse, advertiseCallback)
|
||||||
} catch (se: SecurityException) {
|
} catch (se: SecurityException) {
|
||||||
Log.e(TAG, "SecurityException starting advertising (missing permission?): ${se.message}")
|
Log.e(TAG, "SecurityException starting advertising (missing permission?): ${se.message}")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
Reference in New Issue
Block a user