JuulLabs / kable

Kotlin Asynchronous Bluetooth Low-Energy

Home Page:https://juullabs.github.io/kable

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Connect sometimes throws ConnectionLostException

rocketraman opened this issue · comments

For some reason, peripheral.connect() randomly throws ConnectionLostException:

com.juul.kable.ConnectionLostException
        at com.juul.kable.PeripheralCommon$suspendUntilOrThrow$3.invokeSuspend(Peripheral.kt:250)
        at com.juul.kable.PeripheralCommon$suspendUntilOrThrow$3.invoke(Unknown Source:8)
        at com.juul.kable.PeripheralCommon$suspendUntilOrThrow$3.invoke(Unknown Source:4)
        at kotlinx.coroutines.flow.FlowKt__TransformKt$onEach$$inlined$unsafeTransform$1$2.emit(Emitters.kt:223)
        at kotlinx.coroutines.flow.StateFlowImpl.collect(StateFlow.kt:398)
        at kotlinx.coroutines.flow.StateFlowImpl$collect$1.invokeSuspend(Unknown Source:15)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7405)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:502)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:980)

It could be a buggy device, but I don't know -- when I try to connect again, it works fine. Thought I'd raise it here just in case there is a logic error. For now, I'm working around this with a retry.

I do also note that the javadoc for connect does not declare that it can throw ConnectionLostException, so IDEA soft warns when trying to catch it.

Hard to say exactly what is going wrong without additional logs. Specifically, Android does some logging itself, and it would indicate if it saw a device disconnect. If it did, then there isn't much that can be done on Kable side (we're just responding to the statuses we get), but if there is no such log indicating a connection failure of some kind, then it could very well be a logic error.

Good to know about the KDoc missing the appropriate @throws declaration, I'll update that when I have a chance. 👍

Hard to say exactly what is going wrong without additional logs. Specifically, Android does some logging itself, and it would indicate if it saw a device disconnect.

Seems like Android is seeing a device disconnect with error 133. Googling this seems I am not alone.

Here is one possibly related issue in this project:

#410

and another one in the android connectivity samples:

android/connectivity-samples#18

Here are the logs for your confirmation:

D/BluetoothGatt: connect() - device: F8:F0:05:44:85:6B, auto: false
D/BluetoothGatt: registerApp()
D/BluetoothGatt: registerApp() - UUID=9a47100d-a665-47e6-b2ff-dce391b23770
D/BluetoothGatt: onClientRegistered() - status=0 clientIf=9
D/BleScaleService: BLE peripheral Apex Scale F8:F0:05:44:85:6B state=Connecting.Bluetooth
D/BluetoothGatt: onClientConnectionState() - status=133 clientIf=9 device=F8:F0:05:44:85:6B
D/BluetoothGatt: close()
D/BluetoothGatt: unregisterApp() - mClientIf=9
D/BluetoothGatt: close()
D/BluetoothGatt: unregisterApp() - mClientIf=0
D/BleScaleService: BLE peripheral Apex Scale F8:F0:05:44:85:6B state=Disconnected(133)
D/BleScaleService: BLE peripheral Apex Scale F8:F0:05:44:85:6B disconnected status=Unknown(status=133),  updating device state
D/BluetoothGatt: close()
D/BluetoothGatt: unregisterApp() - mClientIf=0
E/Kable/Peripheral: F8:F0:05:44:85:6B Failed to connect
    
    com.juul.kable.ConnectionLostException
        at com.juul.kable.PeripheralCommon$suspendUntilOrThrow$3.invokeSuspend(Peripheral.kt:250)
        at com.juul.kable.PeripheralCommon$suspendUntilOrThrow$3.invoke(Unknown Source:8)
        at com.juul.kable.PeripheralCommon$suspendUntilOrThrow$3.invoke(Unknown Source:4)
        at kotlinx.coroutines.flow.FlowKt__TransformKt$onEach$$inlined$unsafeTransform$1$2.emit(Emitters.kt:223)
        at kotlinx.coroutines.flow.StateFlowImpl.collect(StateFlow.kt:398)
        at kotlinx.coroutines.flow.StateFlowImpl$collect$1.invokeSuspend(Unknown Source:15)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7405)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:502)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:980)

The retry seems to work consistently, and I see quite a few people recommending that in the connectivity samples issue above.

Ya, there isn't much Kable can do about status 133. My understanding is that reconnecting is the correct action to take in that case.

Although, as you mentioned #410 may alleviate the issue, but autoConnect = false with retry might still be a faster mechanism? 🤷

In any case, I do plan to work on #410 soonish.