mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-26 06:25:20 +00:00
track rssi correctly
This commit is contained in:
@@ -41,12 +41,23 @@ class BluetoothConnectionManager(
|
|||||||
// Delegate for component managers to call back to main manager
|
// Delegate for component managers to call back to main manager
|
||||||
private val componentDelegate = object : BluetoothConnectionManagerDelegate {
|
private val componentDelegate = object : BluetoothConnectionManagerDelegate {
|
||||||
override fun onPacketReceived(packet: BitchatPacket, peerID: String, device: BluetoothDevice?) {
|
override fun onPacketReceived(packet: BitchatPacket, peerID: String, device: BluetoothDevice?) {
|
||||||
|
device?.let { bluetoothDevice ->
|
||||||
|
// Get current RSSI for this device and update if available
|
||||||
|
val currentRSSI = connectionTracker.getBestRSSI(bluetoothDevice.address)
|
||||||
|
if (currentRSSI != null) {
|
||||||
|
delegate?.onRSSIUpdated(bluetoothDevice.address, currentRSSI)
|
||||||
|
}
|
||||||
|
}
|
||||||
delegate?.onPacketReceived(packet, peerID, device)
|
delegate?.onPacketReceived(packet, peerID, device)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDeviceConnected(device: BluetoothDevice) {
|
override fun onDeviceConnected(device: BluetoothDevice) {
|
||||||
delegate?.onDeviceConnected(device)
|
delegate?.onDeviceConnected(device)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onRSSIUpdated(deviceAddress: String, rssi: Int) {
|
||||||
|
delegate?.onRSSIUpdated(deviceAddress, rssi)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val serverManager = BluetoothGattServerManager(
|
private val serverManager = BluetoothGattServerManager(
|
||||||
@@ -231,4 +242,5 @@ class BluetoothConnectionManager(
|
|||||||
interface BluetoothConnectionManagerDelegate {
|
interface BluetoothConnectionManagerDelegate {
|
||||||
fun onPacketReceived(packet: BitchatPacket, peerID: String, device: BluetoothDevice?)
|
fun onPacketReceived(packet: BitchatPacket, peerID: String, device: BluetoothDevice?)
|
||||||
fun onDeviceConnected(device: BluetoothDevice)
|
fun onDeviceConnected(device: BluetoothDevice)
|
||||||
|
fun onRSSIUpdated(deviceAddress: String, rssi: Int)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ class BluetoothConnectionTracker(
|
|||||||
private val subscribedDevices = CopyOnWriteArrayList<BluetoothDevice>()
|
private val subscribedDevices = CopyOnWriteArrayList<BluetoothDevice>()
|
||||||
val addressPeerMap = ConcurrentHashMap<String, String>()
|
val addressPeerMap = ConcurrentHashMap<String, String>()
|
||||||
|
|
||||||
|
// RSSI tracking from scan results (for devices we discover but may connect as servers)
|
||||||
|
private val scanRSSI = ConcurrentHashMap<String, Int>()
|
||||||
|
|
||||||
// Connection attempt tracking with automatic cleanup
|
// Connection attempt tracking with automatic cleanup
|
||||||
private val pendingConnections = ConcurrentHashMap<String, ConnectionAttempt>()
|
private val pendingConnections = ConcurrentHashMap<String, ConnectionAttempt>()
|
||||||
|
|
||||||
@@ -117,6 +120,31 @@ class BluetoothConnectionTracker(
|
|||||||
return subscribedDevices.toList()
|
return subscribedDevices.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current RSSI for a device address
|
||||||
|
*/
|
||||||
|
fun getDeviceRSSI(deviceAddress: String): Int? {
|
||||||
|
return connectedDevices[deviceAddress]?.rssi?.takeIf { it != Int.MIN_VALUE }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store RSSI from scan results
|
||||||
|
*/
|
||||||
|
fun updateScanRSSI(deviceAddress: String, rssi: Int) {
|
||||||
|
scanRSSI[deviceAddress] = rssi
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get best available RSSI for a device (connection RSSI preferred, then scan RSSI)
|
||||||
|
*/
|
||||||
|
fun getBestRSSI(deviceAddress: String): Int? {
|
||||||
|
// Prefer connection RSSI if available and valid
|
||||||
|
connectedDevices[deviceAddress]?.rssi?.takeIf { it != Int.MIN_VALUE }?.let { return it }
|
||||||
|
|
||||||
|
// Fall back to scan RSSI
|
||||||
|
return scanRSSI[deviceAddress]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a subscribed device
|
* Add a subscribed device
|
||||||
*/
|
*/
|
||||||
@@ -249,6 +277,7 @@ class BluetoothConnectionTracker(
|
|||||||
subscribedDevices.clear()
|
subscribedDevices.clear()
|
||||||
addressPeerMap.clear()
|
addressPeerMap.clear()
|
||||||
pendingConnections.clear()
|
pendingConnections.clear()
|
||||||
|
scanRSSI.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -300,6 +329,11 @@ class BluetoothConnectionTracker(
|
|||||||
val elapsed = (now - attempt.lastAttempt) / 1000
|
val elapsed = (now - attempt.lastAttempt) / 1000
|
||||||
appendLine(" - $address: ${attempt.attempts} attempts, last ${elapsed}s ago")
|
appendLine(" - $address: ${attempt.attempts} attempts, last ${elapsed}s ago")
|
||||||
}
|
}
|
||||||
|
appendLine()
|
||||||
|
appendLine("Scan RSSI Cache: ${scanRSSI.size}")
|
||||||
|
scanRSSI.forEach { (address, rssi) ->
|
||||||
|
appendLine(" - $address: $rssi dBm")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,7 @@ import kotlinx.coroutines.CoroutineScope
|
|||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages GATT client operations, scanning, and client-side connections
|
* Manages GATT client operations, scanning, and client-side connections
|
||||||
@@ -31,6 +32,9 @@ class BluetoothGattClientManager(
|
|||||||
// Use exact same UUIDs as iOS version
|
// Use exact same UUIDs as iOS version
|
||||||
private val SERVICE_UUID = UUID.fromString("F47B5E2D-4A9E-4C5A-9B3F-8E1D2C3A4B5C")
|
private val SERVICE_UUID = UUID.fromString("F47B5E2D-4A9E-4C5A-9B3F-8E1D2C3A4B5C")
|
||||||
private val CHARACTERISTIC_UUID = UUID.fromString("A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D")
|
private val CHARACTERISTIC_UUID = UUID.fromString("A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D")
|
||||||
|
|
||||||
|
// RSSI monitoring constants
|
||||||
|
private const val RSSI_UPDATE_INTERVAL = 5000L // 5 seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
// Core Bluetooth components
|
// Core Bluetooth components
|
||||||
@@ -48,6 +52,9 @@ class BluetoothGattClientManager(
|
|||||||
private var isCurrentlyScanning = false
|
private var isCurrentlyScanning = false
|
||||||
private val scanRateLimit = 5000L // Minimum 5 seconds between scan start attempts
|
private val scanRateLimit = 5000L // Minimum 5 seconds between scan start attempts
|
||||||
|
|
||||||
|
// RSSI monitoring state
|
||||||
|
private var rssiMonitoringJob: Job? = null
|
||||||
|
|
||||||
// State management
|
// State management
|
||||||
private var isActive = false
|
private var isActive = false
|
||||||
|
|
||||||
@@ -78,6 +85,9 @@ class BluetoothGattClientManager(
|
|||||||
} else {
|
} else {
|
||||||
startScanning()
|
startScanning()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start RSSI monitoring
|
||||||
|
startRSSIMonitoring()
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@@ -91,6 +101,7 @@ class BluetoothGattClientManager(
|
|||||||
|
|
||||||
connectionScope.launch {
|
connectionScope.launch {
|
||||||
stopScanning()
|
stopScanning()
|
||||||
|
stopRSSIMonitoring()
|
||||||
Log.i(TAG, "GATT client manager stopped")
|
Log.i(TAG, "GATT client manager stopped")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,6 +117,41 @@ class BluetoothGattClientManager(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start periodic RSSI monitoring for all client connections
|
||||||
|
*/
|
||||||
|
private fun startRSSIMonitoring() {
|
||||||
|
rssiMonitoringJob?.cancel()
|
||||||
|
rssiMonitoringJob = connectionScope.launch {
|
||||||
|
while (isActive) {
|
||||||
|
try {
|
||||||
|
// Request RSSI from all client connections
|
||||||
|
val connectedDevices = connectionTracker.getConnectedDevices()
|
||||||
|
connectedDevices.values.filter { it.isClient && it.gatt != null }.forEach { deviceConn ->
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "Requesting RSSI from ${deviceConn.device.address}")
|
||||||
|
deviceConn.gatt?.readRemoteRssi()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "Failed to request RSSI from ${deviceConn.device.address}: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delay(RSSI_UPDATE_INTERVAL)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "Error in RSSI monitoring: ${e.message}")
|
||||||
|
delay(RSSI_UPDATE_INTERVAL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop RSSI monitoring
|
||||||
|
*/
|
||||||
|
private fun stopRSSIMonitoring() {
|
||||||
|
rssiMonitoringJob?.cancel()
|
||||||
|
rssiMonitoringJob = null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start scanning with rate limiting
|
* Start scanning with rate limiting
|
||||||
*/
|
*/
|
||||||
@@ -230,6 +276,9 @@ class BluetoothGattClientManager(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store RSSI from scan results for later use (especially for server connections)
|
||||||
|
connectionTracker.updateScanRSSI(deviceAddress, rssi)
|
||||||
|
|
||||||
// Power-aware RSSI filtering
|
// Power-aware RSSI filtering
|
||||||
if (rssi < powerManager.getRSSIThreshold()) {
|
if (rssi < powerManager.getRSSIThreshold()) {
|
||||||
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()}")
|
||||||
@@ -381,6 +430,21 @@ class BluetoothGattClientManager(
|
|||||||
Log.w(TAG, "Client: Packet data: ${value.joinToString(" ") { "%02x".format(it) }}")
|
Log.w(TAG, "Client: Packet data: ${value.joinToString(" ") { "%02x".format(it) }}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) {
|
||||||
|
val deviceAddress = gatt.device.address
|
||||||
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||||
|
Log.d(TAG, "Client: RSSI updated for $deviceAddress: $rssi dBm")
|
||||||
|
|
||||||
|
// Update the connection tracker with new RSSI value
|
||||||
|
connectionTracker.getDeviceConnection(deviceAddress)?.let { deviceConn ->
|
||||||
|
val updatedConn = deviceConn.copy(rssi = rssi)
|
||||||
|
connectionTracker.updateDeviceConnection(deviceAddress, updatedConn)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Client: Failed to read RSSI for $deviceAddress, status: $status")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class BluetoothGattServerManager(
|
|||||||
isActive = true
|
isActive = true
|
||||||
|
|
||||||
connectionScope.launch {
|
connectionScope.launch {
|
||||||
setupGattServer()
|
// setupGattServer()
|
||||||
delay(300) // Brief delay to ensure GATT server is ready
|
delay(300) // Brief delay to ensure GATT server is ready
|
||||||
startAdvertising()
|
startAdvertising()
|
||||||
}
|
}
|
||||||
@@ -122,8 +122,13 @@ class BluetoothGattServerManager(
|
|||||||
when (newState) {
|
when (newState) {
|
||||||
BluetoothProfile.STATE_CONNECTED -> {
|
BluetoothProfile.STATE_CONNECTED -> {
|
||||||
Log.i(TAG, "Server: Device connected ${device.address}")
|
Log.i(TAG, "Server: Device connected ${device.address}")
|
||||||
|
|
||||||
|
// Get best available RSSI (scan RSSI for server connections)
|
||||||
|
val rssi = connectionTracker.getBestRSSI(device.address) ?: Int.MIN_VALUE
|
||||||
|
|
||||||
val deviceConn = BluetoothConnectionTracker.DeviceConnection(
|
val deviceConn = BluetoothConnectionTracker.DeviceConnection(
|
||||||
device = device,
|
device = device,
|
||||||
|
rssi = rssi,
|
||||||
isClient = false
|
isClient = false
|
||||||
)
|
)
|
||||||
connectionTracker.addDeviceConnection(device.address, deviceConn)
|
connectionTracker.addDeviceConnection(device.address, deviceConn)
|
||||||
|
|||||||
@@ -280,6 +280,13 @@ class BluetoothMeshService(private val context: Context) {
|
|||||||
sendKeyExchangeToDevice()
|
sendKeyExchangeToDevice()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onRSSIUpdated(deviceAddress: String, rssi: Int) {
|
||||||
|
// Find the peer ID for this device address and update RSSI in PeerManager
|
||||||
|
connectionManager.addressPeerMap[deviceAddress]?.let { peerID ->
|
||||||
|
peerManager.updatePeerRSSI(peerID, rssi)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ fun PeopleSection(
|
|||||||
PeerItem(
|
PeerItem(
|
||||||
peerID = peerID,
|
peerID = peerID,
|
||||||
displayName = if (peerID == nickname) "You" else (peerNicknames[peerID] ?: peerID),
|
displayName = if (peerID == nickname) "You" else (peerNicknames[peerID] ?: peerID),
|
||||||
signalStrength = peerRSSI[peerID] ?: 0,
|
signalStrength = convertRSSIToSignalStrength(peerRSSI[peerID]),
|
||||||
isSelected = peerID == selectedPrivatePeer,
|
isSelected = peerID == selectedPrivatePeer,
|
||||||
isFavorite = isFavorite,
|
isFavorite = isFavorite,
|
||||||
hasUnreadDM = hasUnreadPrivateMessages.contains(peerID),
|
hasUnreadDM = hasUnreadPrivateMessages.contains(peerID),
|
||||||
@@ -437,3 +437,25 @@ private fun UnreadBadge(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert RSSI value (dBm) to signal strength percentage (0-100)
|
||||||
|
* RSSI typically ranges from -30 (excellent) to -100 (very poor)
|
||||||
|
* Maps to 0-100 scale where:
|
||||||
|
* - 0-32: No signal (0 bars)
|
||||||
|
* - 33-65: Weak (1 bar)
|
||||||
|
* - 66-98: Good (2 bars)
|
||||||
|
* - 99-100: Excellent (3 bars)
|
||||||
|
*/
|
||||||
|
private fun convertRSSIToSignalStrength(rssi: Int?): Int {
|
||||||
|
if (rssi == null) return 0
|
||||||
|
|
||||||
|
return when {
|
||||||
|
rssi >= -40 -> 100 // Excellent signal
|
||||||
|
rssi >= -55 -> 85 // Very good signal
|
||||||
|
rssi >= -70 -> 70 // Good signal
|
||||||
|
rssi >= -85 -> 50 // Fair signal
|
||||||
|
rssi >= -100 -> 25 // Poor signal
|
||||||
|
else -> 0 // Very poor or no signal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user