From 1a5d91aff3c373e7721ae1935a622321698bcf1d Mon Sep 17 00:00:00 2001 From: Ivo Zivkov Date: Sun, 3 Mar 2024 17:09:46 -0500 Subject: [PATCH] Fix possible deadlock in BLE --- .../org/avmedia/gshockapi/ble/Connection.kt | 12 ++++--- .../avmedia/gshockapi/ble/IGShockManager.kt | 33 +++++++++---------- .../avmedia/gshockapi/casio/CasioConstants.kt | 1 + 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/api/src/main/java/org/avmedia/gshockapi/ble/Connection.kt b/api/src/main/java/org/avmedia/gshockapi/ble/Connection.kt index 70bba6a..d327e66 100644 --- a/api/src/main/java/org/avmedia/gshockapi/ble/Connection.kt +++ b/api/src/main/java/org/avmedia/gshockapi/ble/Connection.kt @@ -12,6 +12,7 @@ import androidx.appcompat.app.AppCompatActivity import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import org.avmedia.gshockapi.ProgressEvents import org.avmedia.gshockapi.WatchInfo import org.avmedia.gshockapi.casio.MessageDispatcher @@ -20,14 +21,14 @@ object Connection { private var bleManager: IGShockManager? = null private val scope = CoroutineScope(Dispatchers.IO) - private suspend fun connect() { + private suspend fun connect(device: BluetoothDevice) { fun onConnected (name: String, address: String) { WatchInfo.setNameAndModel(name.trimEnd('\u0000')) WatchInfo.setAddress(address) } - bleManager?.connect(::onConnected) + bleManager?.connect(device, ::onConnected) } fun disconnect() { @@ -89,12 +90,15 @@ object Connection { } val bluetoothAdapter: BluetoothAdapter? = getDefaultAdapter() val device: BluetoothDevice? = bluetoothAdapter?.getRemoteDevice(address) + if (device == null) { + ProgressEvents.onNext("ApiError", "Cannot obtain remote device") + return + } if (bleManager == null) { bleManager = IGShockManager(context) } - bleManager?.setDevice(device as BluetoothDevice) - connect() + connect(device) } fun isBluetoothEnabled(context:Context): Boolean { diff --git a/api/src/main/java/org/avmedia/gshockapi/ble/IGShockManager.kt b/api/src/main/java/org/avmedia/gshockapi/ble/IGShockManager.kt index 902d6a5..706b7ae 100644 --- a/api/src/main/java/org/avmedia/gshockapi/ble/IGShockManager.kt +++ b/api/src/main/java/org/avmedia/gshockapi/ble/IGShockManager.kt @@ -22,12 +22,11 @@ enum class ConnectionState { typealias onConnectedType = (String, String) -> Unit interface GSHock { - suspend fun connect(onConnected: (String, String) -> Unit) + suspend fun connect(device: BluetoothDevice, onConnected: (String, String) -> Unit) fun release() fun setDataCallback(dataCallback: IDataReceived?) fun enableNotifications() suspend fun write(handle: Int, data: ByteArray) - abstract fun setDevice(device: BluetoothDevice) abstract var connectionState: ConnectionState } @@ -52,8 +51,6 @@ private class GShockManagerImpl( override fun initialize() { super.initialize() - ProgressEvents.onNext("BleManagerInitialized") - setNotificationCallback(writeCharacteristic).with { _, data -> Timber.i("Received data from characteristic: ${data.value}") @@ -64,16 +61,13 @@ private class GShockManagerImpl( } enableNotifications(writeCharacteristic).enqueue() - } - - override fun setDevice(device: BluetoothDevice) { - this.device = device + ProgressEvents.onNext("BleManagerInitialized") } @SuppressLint("MissingPermission") private val scope = CoroutineScope(Dispatchers.IO) - override suspend fun connect(onConnected: onConnectedType) { + override suspend fun connect(device: BluetoothDevice, onConnected: onConnectedType) { this.onConnected = onConnected connect(device) @@ -93,7 +87,9 @@ private class GShockManagerImpl( cancelQueue() // If the device was connected, we have to disconnect manually. - disconnect().enqueue() + if (wasConnected) { + disconnect().enqueue() + } } override fun setDataCallback(dataCallback: IDataReceived?) { @@ -132,8 +128,16 @@ private class GShockManagerImpl( connectionState = ConnectionState.DISCONNECTED } + @SuppressLint("MissingPermission") override fun onDeviceReady(device: BluetoothDevice) { - Timber.i("$device onDeviceReady!!!!!!") + Timber.i("$device DeviceReady!!!!!!") + + onConnected(device.name, device.address) + + // inform the caller that we have connected + ProgressEvents.onNext("ConnectionSetupComplete", device) + ProgressEvents.onNext("DeviceName", device.name) + ProgressEvents.onNext("DeviceAddress", device.address) } override fun onDeviceDisconnecting(device: BluetoothDevice) { @@ -157,13 +161,6 @@ private class GShockManagerImpl( writeCharacteristic = getCharacteristic( CasioConstants.CASIO_ALL_FEATURES_CHARACTERISTIC_UUID, ) - onConnected(device.name, device.address) - - // inform the caller that we have connected - ProgressEvents.onNext("ConnectionSetupComplete", device) - ProgressEvents.onNext("DeviceName", device.name) - ProgressEvents.onNext("DeviceAddress", device.address) - return true } return false diff --git a/api/src/main/java/org/avmedia/gshockapi/casio/CasioConstants.kt b/api/src/main/java/org/avmedia/gshockapi/casio/CasioConstants.kt index f7577a3..5ffe78a 100644 --- a/api/src/main/java/org/avmedia/gshockapi/casio/CasioConstants.kt +++ b/api/src/main/java/org/avmedia/gshockapi/casio/CasioConstants.kt @@ -15,6 +15,7 @@ object CasioConstants { // Modern Watches - All Features val CASIO_READ_REQUEST_FOR_ALL_FEATURES_CHARACTERISTIC_UUID: UUID = UUID.fromString("26eb002c-b012-49a8-b1f8-394fb2032b0f") + val CASIO_ALL_FEATURES_CHARACTERISTIC_UUID: UUID = UUID.fromString("26eb002d-b012-49a8-b1f8-394fb2032b0f")