Android-BLE-Library
Android-BLE-Library copied to clipboard
No response from writeCharacteristic
Hi,
I think I found a bug in writeCharacteristic method. If BLE device disconnects right before the call to writeCharacteristic, then it never returns success or failure.
Example to reproduce:
disconnect()
.done { Log.d("test", "Disconnect request successful") }
.fail { _, _ -> Log.d("test", "Disconnect request failed") }
.enqueue()
writeCharacteristic(
communicationCharacteristic,
byteArrayOf(),
WRITE_TYPE_DEFAULT
)
.done { Log.d("test", "Write request successful") }
.fail { _, _ -> Log.d("test", "Write request failed") }
.invalid { Log.d("test", "Write request invalid") }
.enqueue()
This example prints in the logs Disconnect request successful, but write request neither succeeds nor fails.
I've also noticed the same behavior when BLE device disconnects because of poor signal and the code is trying to write characteristic at the same time.
WhenwriteCharacteristic is used from kotlin with .suspend() like this:
writeCharacteristic(
communicationCharacteristic,
data,
WRITE_TYPE_DEFAULT
).suspend()
it just hangs, never returning or throwing anything. Also, because WriteRequest is not cancellable when suspended, this coroutine can't be cancelled, so wrapping it in withTimeout doesn't help.
I'll have a look, thank you for creating the issue.
@philips77 Have you had a chance to check this topic? I have similar problem, if writeCharacteristic is called after disconnect, none of the done,fail or invalid callbacks are called, what's more, the syncLock underneath is never released, blocking the thread indefinitely.
I didn't have time, sorry.
Hi guys, @Lightwood13 , @dees91 , have you found any solution for this issue?
@32penkin I'm using a workaround like this
suspend fun writeCharacteristic(
characteristic: BluetoothGattCharacteristic,
data: ByteArray,
timeout: Duration
): Unit = withTimeoutOrNull(timeout) {
suspendCancellableCoroutine { continuation ->
writeCharacteristic(
characteristic,
data,
WRITE_TYPE_DEFAULT
)
.done {
if (!continuation.isCancelled) {
continuation.resume(Unit)
}
}
.fail { _, status ->
if (!continuation.isCancelled) {
continuation.resumeWithException(
BLERequestException("Write request failed with status $status")
)
}
}
.enqueue()
}
} ?: throw BLERequestException("Write request timed out")
class BLERequestException(message: String?) : Exception(message)
Here, instead of using library's .suspend() method of WriteRequest, that isn't cancellable, I'm manually suspending execution with suspendCancellableCoroutine and then call .enqueue() inside. If the write request doesn't succeed or fail within given timeout, the withTimeoutOrNull function will cancel the block inside it and return null, so we can handle this case. This method doesn't address the issue with lost resources and the thread that gets blocked indefinitely if request doesn't finish as @dees91 mentioned, but at least it allows to safely use writeCharacteristic from your code without it hanging.
OK, I found the issue. When user requests disconnection, at some point the close() method is called, which clears task list without notifying them in any way.
I'll fix it asap.
Hi, I created a PR that seems to be fixing the issue with tasks not being notified. I'll release it as a beta version and would be more than happy if you could try it.