From 9cd2faee6603cab7bee3c247d8c0e86fde7ce7fe Mon Sep 17 00:00:00 2001 From: Foti Dim Date: Fri, 5 Jul 2024 11:02:33 +0200 Subject: [PATCH] Improve error handling and disconnection events (#60) * Improve Android error handling * Improve cleanup after disconnection on Apple * Update changelog --- CHANGELOG.md | 5 ++ .../universal_ble/UniversalBleHelper.kt | 21 ++++-- .../universal_ble/UniversalBlePlugin.kt | 69 +++++++++++++------ darwin/Classes/UniversalBlePlugin.swift | 18 +++++ pubspec.yaml | 2 +- 5 files changed, 85 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea58dd4..225451b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.10.1 +* Improve Android error handling +* Fix: Android disconnection events were sometimes missed +* Improve cleanup after disconnection on Apple and Android + ## 0.10.0 * BREAKING CHANGE: `ScanResult` is now `BleDevice` * BREAKING CHANGE: `getConnectedDevices` is now `getSystemDevices` diff --git a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt index 5842300..cfb44ba 100644 --- a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt +++ b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt @@ -298,14 +298,14 @@ fun Short.toByteArray(byteOrder: ByteOrder = ByteOrder.LITTLE_ENDIAN): ByteArray fun unknownCharacteristicError(char: String) = FlutterError("IllegalArgument", "Unknown error", null) -// Future result classes -class BleCharacteristicFuture( - val deviceId: String, - val characteristicId: String, - val serviceId: String, - val result: (Result) -> Unit, + +val DeviceDisconnectedError: FlutterError = FlutterError( + "DeviceDisconnected", + "Device Disconnected", + null ) +// Future result classes class DiscoverServicesFuture( val deviceId: String, val result: (Result>) -> Unit, @@ -316,6 +316,13 @@ class MtuResultFuture( val result: (Result) -> Unit, ) +class ReadResultFuture( + val deviceId: String, + val characteristicId: String, + val serviceId: String, + val result: (Result) -> Unit, +) + class WriteResultFuture( val deviceId: String, val characteristicId: String, @@ -323,7 +330,7 @@ class WriteResultFuture( val result: (Result) -> Unit, ) -class CharacteristicSubscriptionFuture( +class SubscriptionResultFuture( val deviceId: String, val characteristicId: String, val serviceId: String, diff --git a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt index 6a99a45..303d2a7 100644 --- a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt +++ b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt @@ -37,12 +37,11 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), private lateinit var context: Context private var activity: Activity? = null private lateinit var bluetoothManager: BluetoothManager - private val mtuResultFutureList = mutableListOf() - private val bleCharacteristicFutureList = mutableListOf() private val discoverServicesFutureList = mutableListOf() - private val characteristicSubscriptionFutureList = - mutableListOf() + private val mtuResultFutureList = mutableListOf() + private val readResultFutureList = mutableListOf() private val writeResultFutureList = mutableListOf() + private val subscriptionResultFutureList = mutableListOf() private val cachedServicesMap = mutableMapOf>() private val devicesStateMap = mutableMapOf() private var bluetoothEnableRequestFuture: ((Result) -> Unit)? = null @@ -240,8 +239,8 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), ) if (gatt.setNotifiable(gattCharacteristic, bleInputProperty)) { - characteristicSubscriptionFutureList.add( - CharacteristicSubscriptionFuture( + subscriptionResultFutureList.add( + SubscriptionResultFuture( gatt.device.address, gattCharacteristic.uuid.toString(), gattCharacteristic.service.uuid.toString(), @@ -296,8 +295,8 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), return } - bleCharacteristicFutureList.add( - BleCharacteristicFuture( + readResultFutureList.add( + ReadResultFuture( gatt.device.address, gattCharacteristic.uuid.toString(), gattCharacteristic.service.uuid.toString(), @@ -325,12 +324,12 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), value: ByteArray, status: Int, ) { - bleCharacteristicFutureList.filter { + readResultFutureList.filter { it.deviceId == gatt.device.address && it.characteristicId == characteristic.uuid.toString() && it.serviceId == characteristic.service.uuid.toString() }.forEach { - bleCharacteristicFutureList.remove(it) + readResultFutureList.remove(it) if (status == BluetoothGatt.GATT_SUCCESS) { it.result(Result.success(value)) } else { @@ -641,14 +640,45 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), private fun cleanConnection(gatt: BluetoothGatt) { knownGatts.remove(gatt) gatt.disconnect() - bleCharacteristicFutureList.removeAll { - it.deviceId == gatt.device.address + readResultFutureList.removeAll { + if (it.deviceId == gatt.device.address) { + it.result(Result.failure(DeviceDisconnectedError)) + true + } else { + false + } + } + writeResultFutureList.removeAll { + if (it.deviceId == gatt.device.address) { + it.result(Result.failure(DeviceDisconnectedError)) + true + } else { + false + } + } + subscriptionResultFutureList.removeAll { + if (it.deviceId == gatt.device.address) { + it.result(Result.failure(DeviceDisconnectedError)) + true + } else { + false + } } mtuResultFutureList.removeAll { - it.deviceId == gatt.device.address + if (it.deviceId == gatt.device.address) { + it.result(Result.failure(DeviceDisconnectedError)) + true + } else { + false + } } discoverServicesFutureList.removeAll { - it.deviceId == gatt.device.address + if (it.deviceId == gatt.device.address) { + it.result(Result.failure(DeviceDisconnectedError)) + true + } else { + false + } } } @@ -752,10 +782,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { devicesStateMap[gatt.device.address] = newState - if (status != BluetoothGatt.GATT_SUCCESS) { - Log.e(TAG, "Failed to update connected state: $status") - return - } + Log.d(TAG, "onConnectionStateChange-> Status: ${status}, NewState: $newState") if (newState == BluetoothGatt.STATE_CONNECTED) { mainThreadHandler?.post { @@ -771,8 +798,6 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), ) {} } } - - } override fun onCharacteristicChanged( @@ -811,12 +836,12 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), service: String, status: Int, ) { - characteristicSubscriptionFutureList.filter { + subscriptionResultFutureList.filter { it.deviceId == deviceId && it.characteristicId == characteristic && it.serviceId == service }.forEach { - characteristicSubscriptionFutureList.remove(it) + subscriptionResultFutureList.remove(it) val error: String? = status.parseGattErrorCode() if (error != null) { it.result( diff --git a/darwin/Classes/UniversalBlePlugin.swift b/darwin/Classes/UniversalBlePlugin.swift index bd9f501..21bb158 100644 --- a/darwin/Classes/UniversalBlePlugin.swift +++ b/darwin/Classes/UniversalBlePlugin.swift @@ -121,6 +121,24 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral } return false } + characteristicWriteFutures.removeAll { future in + if future.deviceId == deviceId { + future.result( + Result.failure(FlutterError(code: "DeviceDisconnected", message: "Device Disconnected", details: nil)) + ) + return true + } + return false + } + characteristicNotifyFutures.removeAll { future in + if future.deviceId == deviceId { + future.result( + Result.failure(FlutterError(code: "DeviceDisconnected", message: "Device Disconnected", details: nil)) + ) + return true + } + return false + } discoverServicesFutures.removeAll { future in if future.deviceId == deviceId { future.result( diff --git a/pubspec.yaml b/pubspec.yaml index 8183bc4..f42a167 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: universal_ble description: A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE) plugin for Flutter -version: 0.10.0 +version: 0.10.1 homepage: https://navideck.com repository: https://github.com/Navideck/universal_ble issue_tracker: https://github.com/Navideck/universal_ble/issues