1

Apologies if what I am asking has been asked before, however despite much searching I have not been able to find any possible explanation to the issue I am experiencing.

I am developing an Android application, which communicates with a BLE Device (CC2541). I am able to write data from Android to BLE device without issues. However issues start when trying to read data from the BLE device in the Android.

I am using Kotlin, and I am trying to "enable" notifications for the particular GATT Characteristic which I want to read, and I do so by setting the descriptor to the following UUID

00002902-0000-1000-8000-00805f9b34fb

And for doing so I have the following code:

private suspend fun setNotification(
    char: BluetoothGattCharacteristic,
    descValue: ByteArray,
    enable: Boolean
) {
    val desc = char.getDescriptor(UUID_CLIENT_CHAR_CONFIG)
        ?: throw IOException("missing config descriptor on $char")
    val key = Pair(char.uuid, desc.uuid)
    if (descWriteCont.containsKey(key))
        throw IllegalStateException("last not finished yet")

    if (!gatt.setCharacteristicNotification(char, enable))
        throw IOException("fail to set notification on $char")

    return suspendCoroutine { cont ->
        descWriteCont[key] = cont
        desc.value = descValue
        if (!gatt.writeDescriptor(desc))
            cont.resumeWithException(IOException("fail to config descriptor $this"))
    }
}

However it just so happens that the following method returns false all the time:

gatt.writeDescriptor(desc)

Does anyone have any idea what could be causing this issue? Apologies in advance if it's a silly question whose answer I have overlooked. I am new to Kotlin and coroutines, and in fact I suspect this issue has to do with how I am using suspend functions.

CTABUYO
  • 662
  • 2
  • 7
  • 27
  • Does this answer your question? [Android BLE BluetoothGatt.writeDescriptor() return sometimes false](https://stackoverflow.com/questions/47097298/android-ble-bluetoothgatt-writedescriptor-return-sometimes-false) – mightyWOZ Jul 29 '21 at 16:12
  • @mightyWOZ Thank you so much for taking the time to comment. However I had seen this before, and in order to be completely sure that only one request at a time was being executed, I did put all my function calls in the same background thread and used runBlocking {} to execute the requests (considering that I have implemented a queue as well). Sadly, it made no difference. And besides, unlike the thread you posted, in my case it is all the time that it returns false. I have yet to see it succeed. – CTABUYO Jul 29 '21 at 16:15
  • If you debug the program by setting a breakpoint at that line and then step into the Android sdk, you will see the exact reason for the method to return false. – Emil Jul 30 '21 at 05:51

1 Answers1

0

I have fixed the issue.

Upon much debugging, I discovered that for some estrange reason (I'm not very experience with Kotlin or Android, so I don't know this reason), the method gatt.writeDescriptor() returns 3 times, (in my case, at least). And only the last time does it return true and the descriptor actually gets written.

So because my code only checked whether it returned true or false the first time, it was obviously failing.

I have now modified my code, to make it wait until it returns true which happens always on the third time it returns.

private suspend fun setNotification(
    char: BluetoothGattCharacteristic,
    descValue: ByteArray,
    enable: Boolean
) {
    val desc = char.getDescriptor(UUID_CLIENT_CHAR_CONFIG)
        ?: throw IOException("missing config descriptor on $char")
    val key = Pair(char.uuid, desc.uuid)
    if (descWriteCont.containsKey(key))
        throw IllegalStateException("last setNotification() not finish")

    if (!gatt.setCharacteristicNotification(char, enable))
        throw IOException("fail to set notification on $char")

    return suspendCoroutine { cont ->
        descWriteCont[key] = cont
        desc.value = descValue
        while (!gatt.writeDescriptor(desc)) {

        }

    }
}

Now I have successfully subscribed for notifications, and can read data from the BLE Device without much issues.

Thanks to all who helped by offering some input, and I hope this will hopefully help someone in the future facing the same situation.

CTABUYO
  • 662
  • 2
  • 7
  • 27
  • That seems like an incorrect solution that will maximize your CPU usage to 100%. I believe you have another pending GATT operation that haven't completed yet (you will get a callback when the operation completes). You must wait for an operation to complete before your can execute another one. – Emil Jul 31 '21 at 07:08
  • @Emil Negative, I even commented out any other GATT Operations (Read/Write). Besides like I said I implemented a queue, and there is nothing in the queue that could explain why the first two times it always returns false :( – CTABUYO Jul 31 '21 at 07:20
  • If you debug the program, you should see what line in https://android.googlesource.com/platform/frameworks/base/+/9eeafe9d52bd4c0c9e03271d8f2c36c6b796b211/core/java/android/bluetooth/BluetoothGatt.java#1351 that returns false. If it is the line `if (mDeviceBusy) return false;` that happens, then you have a pending operation that has not yet completed. The other `return false` can happen if the object is closed, there is no such writable descriptor, or similar. – Emil Jul 31 '21 at 20:49
  • @Emil It returns false in the last part of the code. Line 1276 – CTABUYO Aug 03 '21 at 11:26
  • Line 1276 is not part of the function? – Emil Aug 03 '21 at 13:24
  • @Emil Oops, I am using SDK 30 and the lines numbers seem different. In the link you attached it would be line 1377 where the false is happening. – CTABUYO Aug 03 '21 at 14:11
  • Do you see in logcat what exception is being printed four lines above? – Emil Aug 03 '21 at 14:59
  • @Emil It's extremely weird, cuz by setting a breakpoint I see the Log.e() being triggered. And I also can see that the TAG is BluetoothGatt, however nothing appears in LogCat for that TAG. – CTABUYO Aug 03 '21 at 15:31
  • @Emil So I forgot about the Android app, and went to develop the iOS app where I am expert. I have absolutely the same issue with Objective-C, first two times it can't write the descriptor and only the third time it succeeds. I guess it is related to the quality of this cheapish BLE device rather than Android or Kotlin in itself. – CTABUYO Aug 06 '21 at 08:35
  • I still find it strange. If the write is accepted or not is delivered in onDescriptorWrite. The writeDescriptor methods returns true if the local Bluetooth stack accepted the write request and will send it. – Emil Aug 06 '21 at 08:47