suspend fun withConnection(device: BluetoothDevice, block: suspend Scope.() -> R): R { val channel = Channel(Channel.BUFFERED) val connection = Job(coroutineContext.job) val connected = CompletableDeferred(connection) val callback = object : BluetoothGattCallback() { override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { if (status != BluetoothGatt.GATT_SUCCESS || newState != BluetoothGatt.STATE_CONNECTED) { connection.cancel("Connection failed: $status.") } else if (!gatt.requestMtu(517)) { connection.cancel("MTU request failed.") } } override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) { if (status != BluetoothGatt.GATT_SUCCESS) { connection.cancel("MTU failed: $status.") } else if (!gatt.discoverServices()) { connection.cancel("Service discovery failed.") } } override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { if (status != BluetoothGatt.GATT_SUCCESS) { connection.cancel("Discovery failed: $status.") return } val service = gatt.getService(spService) val characteristic = service?.getCharacteristic(fifoCharacteristic) val descriptor = characteristic?.getDescriptor(cccDescriptor) if (characteristic == null || descriptor == null) { connection.cancel("Invalid Serial Port Service.") return } val writeStatus = gatt.writeDescriptor( descriptor, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE ) if (writeStatus != BluetoothGatt.GATT_SUCCESS) { connection.cancel("Descriptor notification write failed: $writeStatus.") } } override fun onDescriptorWrite( gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int ) { if (status == BluetoothGatt.GATT_SUCCESS) { connected.complete(descriptor.characteristic) } else { connection.cancel("Descriptor notification write failed: $status.") } } override fun onCharacteristicChanged( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray ) { channel.trySend(value).onFailure { connection.cancel("Producer outpaced consumer.", it) } } override fun onCharacteristicWrite( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int ) { if (status != BluetoothGatt.GATT_SUCCESS) { connection.cancel("Write failed: $status.") } } } val gatt = device.connectGatt(null, false, callback) ?: throw Exception("Unable to connect.") connection.invokeOnCompletion { channel.close(it) gatt.disconnect() gatt.close() } val characteristic = try { connected.await() } catch (e: CancellationException) { coroutineContext.ensureActive() throw Exception(e.message, e.cause) } val scope = // ... try { return withContext(connection) { scope.block() } } catch (e: CancellationException) { coroutineContext.ensureActive() throw Exception(e.message, e.cause) } finally { connection.complete() } }