mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-16 19:26:12 -04:00
Improve Bluetooth device connection monitoring
Switched from monitoring generic ACL events to specific Headset profile state changes for more reliable device connection and disconnection detection. This fixes a race condition where CAPod thinks no device is connected because we triggered too early, before "connectedDevices" on the HEADSET profile contains our target device. This fixes #313
This commit is contained in:
@@ -107,12 +107,11 @@ class BluetoothManager2 @Inject constructor(
|
|||||||
profile: Int = BluetoothProfile.HEADSET
|
profile: Int = BluetoothProfile.HEADSET
|
||||||
): Flow<Set<BluetoothDevice>> = getBluetoothProfile(profile).flatMapLatest { bluetoothProfile ->
|
): Flow<Set<BluetoothDevice>> = getBluetoothProfile(profile).flatMapLatest { bluetoothProfile ->
|
||||||
callbackFlow {
|
callbackFlow {
|
||||||
log(TAG, VERBOSE) { "connectedDevices(profile=$profile) starting" }
|
log(TAG, VERBOSE) { "monitorDevices(): for profile=$profile starting" }
|
||||||
trySend(bluetoothProfile.connectedDevices)
|
trySend(bluetoothProfile.connectedDevices)
|
||||||
|
|
||||||
val filter = IntentFilter().apply {
|
val filter = IntentFilter().apply {
|
||||||
addAction(BluetoothDevice.ACTION_ACL_CONNECTED)
|
addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)
|
||||||
addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val handlerThread = HandlerThread("BluetoothEventReceiver").apply {
|
val handlerThread = HandlerThread("BluetoothEventReceiver").apply {
|
||||||
@@ -122,30 +121,60 @@ class BluetoothManager2 @Inject constructor(
|
|||||||
|
|
||||||
val receiver: BroadcastReceiver = object : BroadcastReceiver() {
|
val receiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
log(TAG, VERBOSE) { "Bluetooth event (intent=$intent, extras=${intent.extras})" }
|
log(TAG, VERBOSE) { "monitorDevices(): Bluetooth event (intent=$intent, extras=${intent.extras})" }
|
||||||
val action = intent.action
|
val action = intent.action
|
||||||
if (action == null) {
|
if (action == null) {
|
||||||
log(TAG, ERROR) { "Bluetooth event without action, how did we get this?" }
|
log(TAG, ERROR) { "monitorDevices(): Bluetooth event without action?" }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val device = intent.getParcelableExtra<BluetoothDevice?>(BluetoothDevice.EXTRA_DEVICE)
|
val device = intent.getParcelableExtra<BluetoothDevice?>(BluetoothDevice.EXTRA_DEVICE)
|
||||||
if (device == null) {
|
if (device == null) {
|
||||||
log(TAG, ERROR) { "Connection event is missing EXTRA_DEVICE: ${intent.extras}" }
|
log(TAG, ERROR) { "monitorDevices(): Event is missing EXTRA_DEVICE" }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this@callbackFlow.launch {
|
this@callbackFlow.launch {
|
||||||
val currentDevices = bluetoothProfile.connectedDevices
|
val currentDevices = bluetoothProfile.connectedDevices.toMutableSet()
|
||||||
|
log(TAG) { "monitorDevices(): currentDevices: $currentDevices" }
|
||||||
|
|
||||||
when (action) {
|
if (action != BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED) {
|
||||||
BluetoothDevice.ACTION_ACL_CONNECTED -> {
|
log(TAG, WARN) { "Unknown action: $action" }
|
||||||
log(TAG) { "Adding $device to current devices $currentDevices" }
|
return@launch
|
||||||
trySend(currentDevices.plus(device))
|
}
|
||||||
|
|
||||||
|
// Profile connection changed - query actual state from proxy
|
||||||
|
val statePrevious = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1)
|
||||||
|
log(TAG) { "monitorDevices(): HEADSET profile state changed for $device - previous: $statePrevious" }
|
||||||
|
|
||||||
|
val stateNow = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)
|
||||||
|
log(TAG) { "monitorDevices(): HEADSET profile state changed for $device - now: $stateNow" }
|
||||||
|
|
||||||
|
|
||||||
|
when (stateNow) {
|
||||||
|
BluetoothProfile.STATE_CONNECTING -> {
|
||||||
|
log(TAG) { "monitorDevices(): Currently connecting $device" }
|
||||||
}
|
}
|
||||||
|
|
||||||
BluetoothDevice.ACTION_ACL_DISCONNECTED -> {
|
BluetoothProfile.STATE_CONNECTED -> {
|
||||||
log(TAG) { "Removing $device from current devices $currentDevices" }
|
log(TAG) { "monitorDevices(): Device has connected $device" }
|
||||||
trySend(currentDevices.minus(device))
|
if (!currentDevices.contains(device)) {
|
||||||
|
log(TAG, WARN) { "monitorDevices(): $device was not in $currentDevices" }
|
||||||
|
currentDevices.add(device)
|
||||||
|
}
|
||||||
|
trySend(currentDevices)
|
||||||
|
}
|
||||||
|
|
||||||
|
BluetoothProfile.STATE_DISCONNECTING -> {
|
||||||
|
log(TAG) { "monitorDevices(): Currently DISconnecting $device" }
|
||||||
|
}
|
||||||
|
|
||||||
|
BluetoothProfile.STATE_DISCONNECTED -> {
|
||||||
|
log(TAG) { "monitorDevices(): Device has disconnected $device" }
|
||||||
|
if (!currentDevices.contains(device)) {
|
||||||
|
log(TAG, WARN) { "monitorDevices(): $device WAS in $currentDevices" }
|
||||||
|
currentDevices.remove(device)
|
||||||
|
}
|
||||||
|
trySend(currentDevices)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user