Implement unified and type safe error messages (#186)

* Implement unified and type safe error messages

* Fix Apple error handling

* improve linux exception handling

* improve web exception handling

* fix window imports

* update pigeon generated files

* Fix compilaton issue

* Fix windows error parsing

* Fix windows compilation

* Improve error handling on dart side

* rename _wrapFuture to _executeWithErrorHandling

* Add pigeon generation script and format generated dart file

* Update changelog

* Update readme

---------

Co-authored-by: Foti Dim <foti@navideck.com>
This commit is contained in:
Rohit Sangwan
2025-11-12 00:04:29 +05:30
committed by GitHub
parent b52ca9ee84
commit acfa3200ca
32 changed files with 3399 additions and 1721 deletions
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v22.6.1), do not edit directly.
// Autogenerated from Pigeon (v26.0.5), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
@@ -7,33 +7,67 @@ package com.navideck.universal_ble
import android.util.Log
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MessageCodec
import io.flutter.plugin.common.StandardMethodCodec
import io.flutter.plugin.common.StandardMessageCodec
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
private object UniversalBlePigeonUtils {
private fun wrapResult(result: Any?): List<Any?> {
return listOf(result)
}
fun createConnectionError(channelName: String): FlutterError {
return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "") }
private fun wrapError(exception: Throwable): List<Any?> {
return if (exception is FlutterError) {
listOf(
exception.code,
exception.message,
exception.details
)
} else {
listOf(
exception.javaClass.simpleName,
exception.toString(),
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
)
fun wrapResult(result: Any?): List<Any?> {
return listOf(result)
}
}
private fun createConnectionError(channelName: String): FlutterError {
return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "")}
fun wrapError(exception: Throwable): List<Any?> {
return if (exception is FlutterError) {
listOf(
exception.code,
exception.message,
exception.details
)
} else {
listOf(
exception.javaClass.simpleName,
exception.toString(),
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
)
}
}
fun deepEquals(a: Any?, b: Any?): Boolean {
if (a is ByteArray && b is ByteArray) {
return a.contentEquals(b)
}
if (a is IntArray && b is IntArray) {
return a.contentEquals(b)
}
if (a is LongArray && b is LongArray) {
return a.contentEquals(b)
}
if (a is DoubleArray && b is DoubleArray) {
return a.contentEquals(b)
}
if (a is Array<*> && b is Array<*>) {
return a.size == b.size &&
a.indices.all{ deepEquals(a[it], b[it]) }
}
if (a is List<*> && b is List<*>) {
return a.size == b.size &&
a.indices.all{ deepEquals(a[it], b[it]) }
}
if (a is Map<*, *> && b is Map<*, *>) {
return a.size == b.size && a.all {
(b as Map<Any?, Any?>).contains(it.key) &&
deepEquals(it.value, b[it.key])
}
}
return a == b
}
}
/**
* Error class for passing custom error details to Flutter via a thrown PlatformException.
@@ -47,6 +81,77 @@ class FlutterError (
val details: Any? = null
) : Throwable()
/** Unified error codes for all platforms */
enum class UniversalBleErrorCode(val raw: Int) {
UNKNOWN_ERROR(0),
FAILED(1),
NOT_SUPPORTED(2),
NOT_IMPLEMENTED(3),
CHANNEL_ERROR(4),
BLUETOOTH_NOT_AVAILABLE(5),
BLUETOOTH_NOT_ENABLED(6),
BLUETOOTH_NOT_ALLOWED(7),
BLUETOOTH_UNAUTHORIZED(8),
DEVICE_DISCONNECTED(9),
CONNECTION_TIMEOUT(10),
CONNECTION_FAILED(11),
CONNECTION_REJECTED(12),
CONNECTION_LIMIT_EXCEEDED(13),
CONNECTION_ALREADY_EXISTS(14),
CONNECTION_TERMINATED(15),
CONNECTION_IN_PROGRESS(16),
ILLEGAL_ARGUMENT(17),
DEVICE_NOT_FOUND(18),
SERVICE_NOT_FOUND(19),
CHARACTERISTIC_NOT_FOUND(20),
INVALID_SERVICE_UUID(21),
INVALID_CHARACTERISTIC_UUID(22),
INVALID_OFFSET(23),
INVALID_ATTRIBUTE_LENGTH(24),
INVALID_PDU(25),
INVALID_HANDLE(26),
READ_FAILED(27),
READ_NOT_PERMITTED(28),
WRITE_FAILED(29),
WRITE_NOT_PERMITTED(30),
WRITE_REQUEST_BUSY(31),
INVALID_ACTION(32),
OPERATION_NOT_SUPPORTED(33),
OPERATION_TIMEOUT(34),
OPERATION_CANCELLED(35),
OPERATION_IN_PROGRESS(36),
CHARACTERISTIC_DOES_NOT_SUPPORT_READ(37),
CHARACTERISTIC_DOES_NOT_SUPPORT_WRITE(38),
CHARACTERISTIC_DOES_NOT_SUPPORT_WRITE_WITHOUT_RESPONSE(39),
CHARACTERISTIC_DOES_NOT_SUPPORT_NOTIFY(40),
CHARACTERISTIC_DOES_NOT_SUPPORT_INDICATE(41),
NOT_PAIRED(42),
NOT_PAIRABLE(43),
ALREADY_PAIRED(44),
PAIRING_FAILED(45),
PAIRING_CANCELLED(46),
PAIRING_TIMEOUT(47),
PAIRING_NOT_ALLOWED(48),
AUTHENTICATION_FAILURE(49),
INSUFFICIENT_AUTHENTICATION(50),
INSUFFICIENT_AUTHORIZATION(51),
INSUFFICIENT_ENCRYPTION(52),
INSUFFICIENT_KEY_SIZE(53),
PROTECTION_LEVEL_NOT_MET(54),
ACCESS_DENIED(55),
UNPAIRING_FAILED(56),
ALREADY_UNPAIRED(57),
SCAN_FAILED(58),
STOPPING_SCAN_IN_PROGRESS(59),
WEB_BLUETOOTH_GLOBALLY_DISABLED(60);
companion object {
fun ofRaw(raw: Int): UniversalBleErrorCode? {
return values().firstOrNull { it.raw == raw }
}
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class UniversalBleScanResult (
val deviceId: String,
@@ -78,6 +183,16 @@ data class UniversalBleScanResult (
services,
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalBleScanResult) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -99,6 +214,16 @@ data class UniversalBleService (
characteristics,
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalBleService) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -120,6 +245,16 @@ data class UniversalBleCharacteristic (
properties,
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalBleCharacteristic) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
/**
@@ -148,6 +283,16 @@ data class UniversalScanFilter (
withManufacturerData,
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalScanFilter) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -172,6 +317,16 @@ data class UniversalManufacturerDataFilter (
mask,
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalManufacturerDataFilter) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -193,36 +348,51 @@ data class UniversalManufacturerData (
data,
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalManufacturerData) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
private open class UniversalBlePigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
129.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalBleScanResult.fromList(it)
return (readValue(buffer) as Long?)?.let {
UniversalBleErrorCode.ofRaw(it.toInt())
}
}
130.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalBleService.fromList(it)
UniversalBleScanResult.fromList(it)
}
}
131.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalBleCharacteristic.fromList(it)
UniversalBleService.fromList(it)
}
}
132.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalScanFilter.fromList(it)
UniversalBleCharacteristic.fromList(it)
}
}
133.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalManufacturerDataFilter.fromList(it)
UniversalScanFilter.fromList(it)
}
}
134.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalManufacturerDataFilter.fromList(it)
}
}
135.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalManufacturerData.fromList(it)
}
@@ -232,30 +402,34 @@ private open class UniversalBlePigeonCodec : StandardMessageCodec() {
}
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
when (value) {
is UniversalBleScanResult -> {
is UniversalBleErrorCode -> {
stream.write(129)
writeValue(stream, value.toList())
writeValue(stream, value.raw.toLong())
}
is UniversalBleService -> {
is UniversalBleScanResult -> {
stream.write(130)
writeValue(stream, value.toList())
}
is UniversalBleCharacteristic -> {
is UniversalBleService -> {
stream.write(131)
writeValue(stream, value.toList())
}
is UniversalScanFilter -> {
is UniversalBleCharacteristic -> {
stream.write(132)
writeValue(stream, value.toList())
}
is UniversalManufacturerDataFilter -> {
is UniversalScanFilter -> {
stream.write(133)
writeValue(stream, value.toList())
}
is UniversalManufacturerData -> {
is UniversalManufacturerDataFilter -> {
stream.write(134)
writeValue(stream, value.toList())
}
is UniversalManufacturerData -> {
stream.write(135)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
@@ -302,10 +476,10 @@ interface UniversalBlePlatformChannel {
api.getBluetoothAvailabilityState{ result: Result<Long> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -320,10 +494,10 @@ interface UniversalBlePlatformChannel {
api.enableBluetooth{ result: Result<Boolean> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -338,10 +512,10 @@ interface UniversalBlePlatformChannel {
api.disableBluetooth{ result: Result<Boolean> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -359,7 +533,7 @@ interface UniversalBlePlatformChannel {
api.startScan(filterArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
UniversalBlePigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
@@ -375,7 +549,7 @@ interface UniversalBlePlatformChannel {
api.stopScan()
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
UniversalBlePigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
@@ -393,7 +567,7 @@ interface UniversalBlePlatformChannel {
api.connect(deviceIdArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
UniversalBlePigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
@@ -411,7 +585,7 @@ interface UniversalBlePlatformChannel {
api.disconnect(deviceIdArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
UniversalBlePigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
@@ -431,9 +605,9 @@ interface UniversalBlePlatformChannel {
api.setNotifiable(deviceIdArg, serviceArg, characteristicArg, bleInputPropertyArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
reply.reply(wrapResult(null))
reply.reply(UniversalBlePigeonUtils.wrapResult(null))
}
}
}
@@ -450,10 +624,10 @@ interface UniversalBlePlatformChannel {
api.discoverServices(deviceIdArg) { result: Result<List<UniversalBleService>> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -472,10 +646,10 @@ interface UniversalBlePlatformChannel {
api.readValue(deviceIdArg, serviceArg, characteristicArg) { result: Result<ByteArray> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -493,10 +667,10 @@ interface UniversalBlePlatformChannel {
api.requestMtu(deviceIdArg, expectedMtuArg) { result: Result<Long> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -517,9 +691,9 @@ interface UniversalBlePlatformChannel {
api.writeValue(deviceIdArg, serviceArg, characteristicArg, valueArg, bleOutputPropertyArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
reply.reply(wrapResult(null))
reply.reply(UniversalBlePigeonUtils.wrapResult(null))
}
}
}
@@ -536,10 +710,10 @@ interface UniversalBlePlatformChannel {
api.isPaired(deviceIdArg) { result: Result<Boolean> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -556,10 +730,10 @@ interface UniversalBlePlatformChannel {
api.pair(deviceIdArg) { result: Result<Boolean> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -577,7 +751,7 @@ interface UniversalBlePlatformChannel {
api.unPair(deviceIdArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
UniversalBlePigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
@@ -594,10 +768,10 @@ interface UniversalBlePlatformChannel {
api.getSystemDevices(withServicesArg) { result: Result<List<UniversalBleScanResult>> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -614,7 +788,7 @@ interface UniversalBlePlatformChannel {
val wrapped: List<Any?> = try {
listOf(api.getConnectionState(deviceIdArg))
} catch (exception: Throwable) {
wrapError(exception)
UniversalBlePigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
@@ -650,7 +824,7 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
@@ -667,7 +841,7 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
@@ -684,7 +858,7 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
@@ -701,7 +875,7 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
@@ -718,7 +892,7 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
@@ -2,11 +2,8 @@ package com.navideck.universal_ble
import android.annotation.SuppressLint
import android.bluetooth.le.ScanFilter
import android.bluetooth.le.ScanResult
import android.os.ParcelUuid
import android.util.Log
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.UUID
import kotlin.experimental.and
@@ -46,7 +43,7 @@ class UniversalBleFilterUtil {
}
private fun isNameMatchingFilters(scanFilter: UniversalScanFilter, name: String?): Boolean {
val namePrefixFilter = scanFilter.withNamePrefix.filterNotNull()
val namePrefixFilter = scanFilter.withNamePrefix
if (namePrefixFilter.isEmpty()) {
return true
}
@@ -74,7 +71,7 @@ class UniversalBleFilterUtil {
scanFilter: UniversalScanFilter,
manufacturerDataList: List<UniversalManufacturerData>,
): Boolean {
val filters = scanFilter.withManufacturerData.filterNotNull()
val filters = scanFilter.withManufacturerData
if (filters.isEmpty()) return true
if (manufacturerDataList.isEmpty()) return false
return manufacturerDataList.any { msd ->
@@ -122,8 +119,8 @@ fun UniversalScanFilter.toScanFilters(serviceUuids: List<UUID>): List<ScanFilter
}
} catch (e: Exception) {
Log.e(TAG, e.toString())
throw FlutterError(
"illegalIllegalArgument",
throw createFlutterError(
UniversalBleErrorCode.INVALID_SERVICE_UUID,
"Invalid serviceId: $service",
e.toString()
)
@@ -133,7 +130,7 @@ fun UniversalScanFilter.toScanFilters(serviceUuids: List<UUID>): List<ScanFilter
// Add ManufacturerData Filter
for (manufacturerData in this.withManufacturerData) {
try {
manufacturerData?.companyIdentifier?.let {
manufacturerData.companyIdentifier.let {
val data: ByteArray = manufacturerData.data ?: ByteArray(0)
val mask: ByteArray? = manufacturerData.mask
if (mask == null) {
@@ -152,9 +149,9 @@ fun UniversalScanFilter.toScanFilters(serviceUuids: List<UUID>): List<ScanFilter
}
} catch (e: Exception) {
Log.e(TAG, e.toString())
throw FlutterError(
"illegalIllegalArgument",
"Invalid manufacturerData: ${manufacturerData?.companyIdentifier} ${manufacturerData?.data} ${manufacturerData?.mask}",
throw createFlutterError(
UniversalBleErrorCode.FAILED,
"Invalid manufacturerData: ${manufacturerData.companyIdentifier} ${manufacturerData.data} ${manufacturerData.mask}",
e.toString()
)
}
@@ -17,6 +17,7 @@ import android.util.SparseArray
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.UUID
import androidx.core.util.size
private const val TAG = "UniversalBlePlugin"
@@ -46,8 +47,8 @@ enum class BleInputProperty(val value: Long) {
}
enum class BleOutputProperty(val value: Long) {
withResponse(0),
withoutResponse(1);
WithResponse(0),
WithoutResponse(1);
}
@@ -80,7 +81,10 @@ fun List<String>.toUUIDList(): List<UUID> {
fun String.toBluetoothGatt(): BluetoothGatt {
return this.findGatt()
?: throw FlutterError("IllegalArgument", "Unknown deviceId: $this", null)
?: throw createFlutterError(
UniversalBleErrorCode.DEVICE_NOT_FOUND,
"Unknown deviceId: $this",
)
}
fun String.isKnownGatt(): Boolean {
@@ -122,53 +126,6 @@ fun Int.parseScanErrorMessage(): String {
}
}
fun Int.parseBluetoothStatusCodeError(): String? {
if (this == BluetoothStatusCodes.SUCCESS) return null
return when (this) {
BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED -> "ERROR_BLUETOOTH_NOT_ENABLED"
BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED -> "ERROR_BLUETOOTH_NOT_ALLOWED"
BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED -> "ERROR_DEVICE_NOT_BONDED"
BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED -> "ERROR_GATT_WRITE_NOT_ALLOWED"
BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY -> "ERROR_GATT_WRITE_REQUEST_BUSY"
BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION -> "ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION"
BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND -> "ERROR_PROFILE_SERVICE_NOT_BOUND"
BluetoothStatusCodes.ERROR_UNKNOWN -> "ERROR_UNKNOWN"
BluetoothStatusCodes.FEATURE_NOT_CONFIGURED -> "FEATURE_NOT_CONFIGURED"
BluetoothStatusCodes.FEATURE_NOT_SUPPORTED -> "FEATURE_NOT_SUPPORTED"
BluetoothStatusCodes.FEATURE_SUPPORTED -> "FEATURE_SUPPORTED"
else -> "ErrorCode: $this"
}
}
fun Int.parseGattErrorCode(): String? {
return when (this) {
BluetoothGatt.GATT_SUCCESS -> null
BluetoothGatt.GATT_READ_NOT_PERMITTED -> "GATT_READ_NOT_PERMITTED"
BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> "GATT_WRITE_NOT_PERMITTED"
BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION -> "GATT_INSUFFICIENT_AUTHENTICATION"
BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED -> "GATT_REQUEST_NOT_SUPPORTED"
BluetoothGatt.GATT_INVALID_OFFSET -> "GATT_INVALID_OFFSET"
BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION -> "GATT_INSUFFICIENT_AUTHORIZATION"
BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH -> "GATT_INVALID_ATTRIBUTE_LENGTH"
BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION -> "GATT_INSUFFICIENT_ENCRYPTION"
BluetoothGatt.GATT_CONNECTION_CONGESTED -> "GATT_CONNECTION_CONGESTED"
BluetoothGatt.GATT_FAILURE -> "GATT_FAILURE"
BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED -> "ERROR_GATT_WRITE_NOT_ALLOWED"
BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY -> "ERROR_GATT_WRITE_REQUEST_BUSY"
BluetoothStatusCodes.FEATURE_NOT_CONFIGURED -> "FEATURE_NOT_CONFIGURED"
0x01 -> "GATT_INVALID_HANDLE"
0x04 -> "GATT_INVALID_PDU"
0x09 -> "GATT_PREPARE_QUEUE_FULL"
0x0a -> "GATT_ATTR_NOT_FOUND"
0x0b -> "GATT_ATTR_NOT_LONG"
0x0c -> "GATT_INSUFFICIENT_KEY_SIZE"
0x0e -> "GATT_UNLIKELY"
0x10 -> "GATT_UNSUPPORTED_GROUP"
0x11 -> "GATT_INSUFFICIENT_RESOURCES"
else -> "Unknown Error: $this"
}
}
val ScanResult.manufacturerDataList: List<UniversalManufacturerData>
get() {
return scanRecord?.manufacturerSpecificData?.toList()?.map { (key, value) ->
@@ -177,7 +134,7 @@ val ScanResult.manufacturerDataList: List<UniversalManufacturerData>
}
fun <T> SparseArray<T>.toList(): List<Pair<Int, T>> {
return (0 until size()).map { index ->
return (0 until size).map { index ->
keyAt(index) to valueAt(index)
}
}
@@ -189,16 +146,6 @@ fun BluetoothGatt.getCharacteristic(
return getService(UUID.fromString(service))?.getCharacteristic(UUID.fromString(characteristic))
}
fun subscriptionFailedError(error: String? = null): Result<Unit> {
return Result.failure(
FlutterError(
"Failed",
"Failed to update subscription state",
error
)
)
}
fun BluetoothDevice.removeBond() {
try {
javaClass.getMethod("removeBond").invoke(this)
@@ -207,7 +154,6 @@ fun BluetoothDevice.removeBond() {
}
}
fun BluetoothGattCharacteristic.getPropertiesList(): ArrayList<Long> {
val propertiesList = arrayListOf<Long>()
if (properties and BluetoothGattCharacteristic.PROPERTY_BROADCAST > 0) {
@@ -241,16 +187,54 @@ fun BluetoothGattCharacteristic.getPropertiesList(): ArrayList<Long> {
fun Short.toByteArray(byteOrder: ByteOrder = ByteOrder.LITTLE_ENDIAN): ByteArray =
ByteBuffer.allocate(2 /*Short.SIZE_BYTES*/).order(byteOrder).putShort(this).array()
// Errors
fun unknownCharacteristicError(char: String) =
FlutterError("IllegalArgument", "Unknown error", null)
fun createFlutterError(
code: UniversalBleErrorCode,
message: String? = null,
details: String? = null,
) = FlutterError(code.raw.toString(), message, details ?: code.toString())
val DeviceDisconnectedError: FlutterError = FlutterError(
"DeviceDisconnected",
"Device Disconnected",
null
)
fun gattStatusToUniversalBleErrorCode(code: Int): UniversalBleErrorCode {
return when (code) {
BluetoothGatt.GATT_READ_NOT_PERMITTED -> UniversalBleErrorCode.READ_NOT_PERMITTED
BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> UniversalBleErrorCode.WRITE_NOT_PERMITTED
BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION -> UniversalBleErrorCode.INSUFFICIENT_AUTHENTICATION
BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION -> UniversalBleErrorCode.INSUFFICIENT_AUTHORIZATION
BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION -> UniversalBleErrorCode.INSUFFICIENT_ENCRYPTION
BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED -> UniversalBleErrorCode.OPERATION_NOT_SUPPORTED
BluetoothGatt.GATT_INVALID_OFFSET -> UniversalBleErrorCode.INVALID_OFFSET
BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH -> UniversalBleErrorCode.INVALID_ATTRIBUTE_LENGTH
BluetoothGatt.GATT_CONNECTION_CONGESTED -> UniversalBleErrorCode.CONNECTION_FAILED
BluetoothGatt.GATT_FAILURE -> UniversalBleErrorCode.FAILED
0x01 -> UniversalBleErrorCode.INVALID_HANDLE
0x04 -> UniversalBleErrorCode.INVALID_PDU
0x09 -> UniversalBleErrorCode.OPERATION_IN_PROGRESS
0x0a -> UniversalBleErrorCode.SERVICE_NOT_FOUND
0x0b -> UniversalBleErrorCode.INVALID_ATTRIBUTE_LENGTH
0x0c -> UniversalBleErrorCode.INSUFFICIENT_KEY_SIZE
0x0e -> UniversalBleErrorCode.FAILED
0x10 -> UniversalBleErrorCode.OPERATION_NOT_SUPPORTED
0x11 -> UniversalBleErrorCode.FAILED
else -> UniversalBleErrorCode.UNKNOWN_ERROR
}
}
fun Int.parseBluetoothStatusCodeError(): UniversalBleErrorCode? {
if (this == BluetoothStatusCodes.SUCCESS) return null
return when (this) {
BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED -> UniversalBleErrorCode.BLUETOOTH_NOT_ENABLED
BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED -> UniversalBleErrorCode.BLUETOOTH_NOT_ALLOWED
BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED -> UniversalBleErrorCode.NOT_PAIRED
BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED -> UniversalBleErrorCode.WRITE_NOT_PERMITTED
BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY -> UniversalBleErrorCode.WRITE_REQUEST_BUSY
BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION -> UniversalBleErrorCode.CONNECTION_FAILED
BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND -> UniversalBleErrorCode.SERVICE_NOT_FOUND
BluetoothStatusCodes.ERROR_UNKNOWN -> UniversalBleErrorCode.UNKNOWN_ERROR
BluetoothStatusCodes.FEATURE_NOT_CONFIGURED -> UniversalBleErrorCode.NOT_IMPLEMENTED
BluetoothStatusCodes.FEATURE_NOT_SUPPORTED -> UniversalBleErrorCode.NOT_SUPPORTED
else -> null
}
}
fun Int.parseHciErrorCode(): String? {
return when (this) {
@@ -32,6 +32,7 @@ import io.flutter.plugin.common.PluginRegistry
import java.util.UUID
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import androidx.core.content.edit
private const val TAG = "UniversalBlePlugin"
@@ -102,7 +103,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
if (bluetoothEnableRequestFuture != null) {
callback(
Result.failure(
FlutterError("Failed", "Bluetooth enable request in progress", null)
createFlutterError(
UniversalBleErrorCode.OPERATION_IN_PROGRESS,
"Bluetooth enable request in progress"
)
)
)
return
@@ -120,7 +124,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
if (bluetoothDisableRequestFuture != null) {
callback(
Result.failure(
FlutterError("Failed", "Bluetooth disable request in progress", null)
createFlutterError(
UniversalBleErrorCode.OPERATION_IN_PROGRESS,
"Bluetooth disable request in progress"
)
)
)
return
@@ -131,9 +138,9 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
}
override fun startScan(filter: UniversalScanFilter?) {
if (!isBluetoothAvailable()) throw FlutterError(
"BluetoothNotEnabled",
"Bluetooth not enabled",
if (!isBluetoothAvailable()) throw createFlutterError(
UniversalBleErrorCode.BLUETOOTH_NOT_ENABLED,
"Bluetooth not enabled"
)
val builder = ScanSettings.Builder()
@@ -143,10 +150,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
}
val settings = builder.build()
val usesCustomFilters = filter?.usesCustomFilters() ?: false;
val usesCustomFilters = filter?.usesCustomFilters() ?: false
try {
val filterServices = filter?.withServices?.filterNotNull()?.toUUIDList() ?: emptyList()
val filterServices = filter?.withServices?.toUUIDList() ?: emptyList()
var scanFilters = emptyList<ScanFilter>()
// Set custom scan filter only if required
@@ -156,25 +163,25 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
universalBleFilterUtil.serviceFilterUUIDS = filterServices
} else {
universalBleFilterUtil.scanFilter = null
scanFilters = filter?.toScanFilters(filterServices) ?: emptyList<ScanFilter>()
scanFilters = filter?.toScanFilters(filterServices) ?: emptyList()
}
safeScanner.startScan(
scanFilters, settings, scanCallback
)
} catch (e: Exception) {
throw FlutterError(
"illegalIllegalArgument",
throw createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to start Scan",
e.toString()
details = e.toString()
)
}
}
override fun stopScan() {
if (!isBluetoothAvailable()) throw FlutterError(
"BluetoothNotEnabled",
"Bluetooth not enabled",
if (!isBluetoothAvailable()) throw createFlutterError(
UniversalBleErrorCode.BLUETOOTH_NOT_ENABLED,
"Bluetooth not enabled"
)
// check if already scanning
safeScanner.stopScan(scanCallback)
@@ -192,7 +199,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
}
return
} else if (currentState == BluetoothGatt.STATE_CONNECTING) {
throw FlutterError("Connecting", "Connection already in progress", null)
throw createFlutterError(
UniversalBleErrorCode.CONNECTION_IN_PROGRESS,
"Connection already in progress"
)
}
}
@@ -240,7 +250,12 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
discoverServicesFutureList.add(DiscoverServicesFuture(deviceId, callback))
} else {
callback(
Result.failure(FlutterError("Failed", "Failed to discover services", null))
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to discover services"
)
)
)
}
} catch (e: FlutterError) {
@@ -253,7 +268,12 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
discoverServicesFutureList.filter { it.deviceId == gatt.device.address }.forEach {
discoverServicesFutureList.remove(it)
it.result(
Result.failure(FlutterError("Failed", "Failed to discover services", null))
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to discover services"
)
)
)
}
return
@@ -290,7 +310,14 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
gatt.getCharacteristic(service, characteristic)
if (gattCharacteristic == null) {
callback(subscriptionFailedError("characteristic not found"))
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.CHARACTERISTIC_NOT_FOUND,
"characteristic not found"
)
)
)
return
}
@@ -298,7 +325,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
gattCharacteristic.getDescriptor(ccdCharacteristic)
val bleInputPropertyEnum: BleInputProperty =
BleInputProperty.values().first { it.value == bleInputProperty }
BleInputProperty.entries.first { it.value == bleInputProperty }
val (value, enable) = when (bleInputPropertyEnum) {
BleInputProperty.Notification -> BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE to true
@@ -310,14 +337,30 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
// Some devices do not need CCCD to update
@Suppress("DEPRECATION")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val status = gatt.writeDescriptor(descriptor, value)
if (gatt.writeDescriptor(descriptor, value) != BluetoothStatusCodes.SUCCESS) {
callback(subscriptionFailedError("Failed to update descriptor"))
callback(
Result.failure(
createFlutterError(
status.parseBluetoothStatusCodeError()
?: UniversalBleErrorCode.FAILED,
"Failed to update descriptor"
)
)
)
return
}
} else {
descriptor.value = value
if (!gatt.writeDescriptor(descriptor)) {
callback(subscriptionFailedError("Failed to update descriptor"))
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to update descriptor"
)
)
)
return
}
}
@@ -339,12 +382,27 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
callback(Result.success(Unit))
}
} else {
callback(subscriptionFailedError())
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to update subscription state"
)
)
)
}
} catch (e: FlutterError) {
callback(Result.failure(e))
} catch (e: Exception) {
callback(subscriptionFailedError(e.toString()))
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to update subscription state",
e.toString()
)
)
)
}
}
@@ -359,13 +417,23 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
val gattCharacteristic = gatt.getCharacteristic(service, characteristic)
if (gattCharacteristic == null) {
callback(
Result.failure(FlutterError("IllegalArgument", "Unknown characteristic", null))
Result.failure(
createFlutterError(
UniversalBleErrorCode.CHARACTERISTIC_NOT_FOUND,
"Unknown characteristic"
)
)
)
return
}
if (!gatt.readCharacteristic(gattCharacteristic)) {
callback(
Result.failure(unknownCharacteristicError(characteristic))
Result.failure(
createFlutterError(
UniversalBleErrorCode.CHARACTERISTIC_NOT_FOUND,
"$characteristic not found",
)
)
)
return
}
@@ -383,8 +451,8 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
} catch (e: Exception) {
callback(
Result.failure(
FlutterError(
"Failed",
createFlutterError(
UniversalBleErrorCode.READ_FAILED,
"Failed to read value",
e.toString()
)
@@ -410,10 +478,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
} else {
it.result(
Result.failure(
FlutterError(
status.toString(),
"Failed to read: (${status.parseGattErrorCode()})",
null,
createFlutterError(
gattStatusToUniversalBleErrorCode(status),
"Failed to read",
status.toString()
)
)
)
@@ -434,33 +502,38 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
val gatt = deviceId.toBluetoothGatt()
val gattCharacteristic = gatt.getCharacteristic(service, characteristic)
if (gattCharacteristic == null) {
callback(Result.failure(unknownCharacteristicError(characteristic)))
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.CHARACTERISTIC_NOT_FOUND,
"$characteristic not found",
)
)
)
return
}
var writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
if (bleOutputProperty == BleOutputProperty.withResponse.value) {
if (bleOutputProperty == BleOutputProperty.WithResponse.value) {
if (gattCharacteristic.properties and BluetoothGattCharacteristic.PROPERTY_WRITE == 0) {
callback(
Result.failure(
FlutterError(
"IllegalArgument",
"Characteristic does not support write withResponse",
null
createFlutterError(
UniversalBleErrorCode.CHARACTERISTIC_DOES_NOT_SUPPORT_WRITE,
"Characteristic does not support write withResponse"
)
)
)
return
}
writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
} else if (bleOutputProperty == BleOutputProperty.withoutResponse.value) {
} else if (bleOutputProperty == BleOutputProperty.WithoutResponse.value) {
if (gattCharacteristic.properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE == 0) {
callback(
Result.failure(
FlutterError(
"IllegalArgument",
"Characteristic does not support write withoutResponse",
null
createFlutterError(
UniversalBleErrorCode.CHARACTERISTIC_DOES_NOT_SUPPORT_WRITE_WITHOUT_RESPONSE,
"Characteristic does not support write withoutResponse"
)
)
)
@@ -495,10 +568,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
writeResultFutureList.remove(writeFuture)
callback(
Result.failure(
FlutterError(
"WriteError",
"Failed to write: ${result.parseGattErrorCode()}",
null
createFlutterError(
gattStatusToUniversalBleErrorCode(result),
"Failed to write",
result.toString()
)
)
)
@@ -524,10 +597,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
} else {
it.result(
Result.failure(
FlutterError(
status.toString(),
"Failed to write: (${status.parseGattErrorCode()})",
null,
createFlutterError(
gattStatusToUniversalBleErrorCode(status),
"Failed to write",
status.toString()
)
)
)
@@ -553,7 +626,14 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
if (status == BluetoothGatt.GATT_SUCCESS) {
it.result(Result.success(mtu.toLong()))
} else {
it.result(Result.failure(FlutterError("Failed to change MTU", null, null)))
it.result(
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to change MTU"
)
)
)
}
}
}
@@ -580,10 +660,9 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
if (pendingFuture != null) {
callback(
Result.failure(
FlutterError(
"InProgress",
"Pairing already in progress",
null
createFlutterError(
UniversalBleErrorCode.OPERATION_IN_PROGRESS,
"Pairing already in progress"
)
)
)
@@ -594,12 +673,19 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
if (remoteDevice.createBond()) {
pairResultFutures[deviceId] = callback
} else {
callback(Result.failure(FlutterError("Failed", "Failed to pair", null)))
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.PAIRING_FAILED,
"Failed to pair"
)
)
)
}
} catch (e: Exception) {
callback(
Result.failure(
FlutterError("Failed", e.toString(), null)
createFlutterError(UniversalBleErrorCode.FAILED, e.toString())
)
)
}
@@ -664,7 +750,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
try {
val timeout = (devices.size * 2).toLong()
latch.await(timeout, TimeUnit.SECONDS)
} catch (e: InterruptedException) {
} catch (_: InterruptedException) {
Thread.currentThread().interrupt()
}
@@ -749,10 +835,13 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
private fun cleanConnection(gatt: BluetoothGatt) {
gatt.removeCache()
gatt.disconnect()
val deviceDisconnectedError: FlutterError = createFlutterError(
UniversalBleErrorCode.DEVICE_DISCONNECTED,
"Device Disconnected",
)
readResultFutureList.removeAll {
if (it.deviceId == gatt.device.address) {
it.result(Result.failure(DeviceDisconnectedError))
it.result(Result.failure(deviceDisconnectedError))
true
} else {
false
@@ -760,7 +849,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
}
writeResultFutureList.removeAll {
if (it.deviceId == gatt.device.address) {
it.result(Result.failure(DeviceDisconnectedError))
it.result(Result.failure(deviceDisconnectedError))
true
} else {
false
@@ -768,7 +857,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
}
subscriptionResultFutureList.removeAll {
if (it.deviceId == gatt.device.address) {
it.result(Result.failure(DeviceDisconnectedError))
it.result(Result.failure(deviceDisconnectedError))
true
} else {
false
@@ -776,7 +865,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
}
mtuResultFutureList.removeAll {
if (it.deviceId == gatt.device.address) {
it.result(Result.failure(DeviceDisconnectedError))
it.result(Result.failure(deviceDisconnectedError))
true
} else {
false
@@ -784,7 +873,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
}
discoverServicesFutureList.removeAll {
if (it.deviceId == gatt.device.address) {
it.result(Result.failure(DeviceDisconnectedError))
it.result(Result.failure(deviceDisconnectedError))
true
} else {
false
@@ -830,7 +919,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
Log.v(TAG, "${device.address} BOND_BONDING")
}
BluetoothDevice.BOND_BONDED -> {
BOND_BONDED -> {
onBondStateUpdate(device.address, true)
}
@@ -944,7 +1033,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
if (descriptor?.uuid.toString() == ccdCharacteristic.toString()) {
val char: String? = descriptor?.characteristic?.uuid?.toString()
val service: String? = descriptor?.characteristic?.service?.uuid?.toString()
val deviceId: String? = gatt?.device?.address;
val deviceId: String? = gatt?.device?.address
if (deviceId != null && char != null && service != null) {
updateSubscriptionState(deviceId, char, service, status)
}
@@ -963,14 +1052,13 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
it.serviceId == service
}.forEach {
subscriptionResultFutureList.remove(it)
val error: String? = status.parseGattErrorCode()
if (error != null) {
if (status != BluetoothGatt.GATT_SUCCESS) {
it.result(
Result.failure(
FlutterError(
status.toString(),
"Failed to update subscription state: $error",
null,
createFlutterError(
gattStatusToUniversalBleErrorCode(status),
"Failed to update subscription state",
status.toString()
)
)
)
@@ -989,7 +1077,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
"com.navideck.universal_ble.services",
Context.MODE_PRIVATE
)
cachedServicesSharedPref.edit().putStringSet(deviceId, services.toSet()).apply()
cachedServicesSharedPref.edit { putStringSet(deviceId, services.toSet()) }
cachedServicesMap[deviceId] = services
}