Beepiz / BleGattCoroutines

Functional Bluetooth GATT for Android (all the meanings)
Apache License 2.0
470 stars 52 forks source link

Receive channel's poll fails when dicoverServices is called and keeps app stuck #57

Open odahcam opened 4 years ago

odahcam commented 4 years ago

When we call our device's deviceConnection.discoverServices() it calls the method written at line 317 of com/beepiz/bluetooth/gattcoroutines/GattConnectionImpl.kt, gattRequest. This method receives a channel (ch: ReceiveChannel<GattResponse<E>>) for the request purpose and after a few instructions calls ch.reveice() on this channel. That's when the problem occours.

The channel throws an error internally the polls fails (as the following print shows) and the exceution goes all the levels up, like when an exception is thrown, but this time without any clue of an error being thrown: image

These are the last captured logs:

D/BluetoothAdapter: STATE_ON
D/BluetoothGatt: connect() - device: 00:15:90:91:CE:C2, auto: false
D/BluetoothAdapter: isSecureModeEnabled
D/BluetoothGatt: registerApp()
D/BluetoothGatt: registerApp() - UUID=5ab39c56-c260-4491-9cac-58843c486207
D/BluetoothGatt: onClientRegistered() - status=0 clientIf=8
D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=8 device=00:15:90:91:CE:C2
D/BluetoothGatt: onConnectionUpdated() - Device=00:15:90:91:CE:C2 interval=16 latency=0 timeout=800 status=0
D/BluetoothGatt: onConnectionUpdated() - Device=00:15:90:91:CE:C2 interval=6 latency=0 timeout=500 status=0
D/BluetoothGatt: onConnectionUpdated() - Device=00:15:90:91:CE:C2 interval=16 latency=0 timeout=800 status=0
D/BluetoothGatt: discoverServices() - device: 00:15:90:91:CE:C2
D/BluetoothGatt: onSearchComplete() = Device=00:15:90:91:CE:C2 Status=0

I tried to capture any thrown exception around the discoverServices() call, but the cursor never comes back to my code after that ch.receive() call.

LouisCAD commented 4 years ago

Poll failing is normal, it means the device didn't return the services yet. The function should resume later when the device finally returns its services. Note that the debugger cannot currently step over suspend calls. You need to put the breakpoint after instead of stepping until it.

Did you try putting a breakpoint in your code on the code after discoverServices?

On Fri, Jan 17, 2020, 19:52 Luiz Machado notifications@github.com wrote:

When we call our device's deviceConnection.discoverServices() it calls the method written at line 317 of com/beepiz/bluetooth/gattcoroutines/GattConnectionImpl.kt, gattRequest. This method receives a channel (ch: ReceiveChannel<GattResponse>) for the request purpose and after a few instructions calls ch.reveice() on this channel. That's when the problem occours.

The channel throws an error internally the polls fails (as the following print shows) and the exceution goes all the levels up, like when an exception is thrown, but this time without any clue of an error being thrown: [image: image] https://user-images.githubusercontent.com/3942006/72637560-60074200-3940-11ea-8171-0d456437970d.png

These are the last captured logs:

D/BluetoothAdapter: STATE_ON D/BluetoothGatt: connect() - device: 00:15:90:91:CE:C2, auto: false D/BluetoothAdapter: isSecureModeEnabled D/BluetoothGatt: registerApp() D/BluetoothGatt: registerApp() - UUID=5ab39c56-c260-4491-9cac-58843c486207 D/BluetoothGatt: onClientRegistered() - status=0 clientIf=8 D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=8 device=00:15:90:91:CE:C2 D/BluetoothGatt: onConnectionUpdated() - Device=00:15:90:91:CE:C2 interval=16 latency=0 timeout=800 status=0 D/BluetoothGatt: onConnectionUpdated() - Device=00:15:90:91:CE:C2 interval=6 latency=0 timeout=500 status=0 D/BluetoothGatt: onConnectionUpdated() - Device=00:15:90:91:CE:C2 interval=16 latency=0 timeout=800 status=0 D/BluetoothGatt: discoverServices() - device: 00:15:90:91:CE:C2 D/BluetoothGatt: onSearchComplete() = Device=00:15:90:91:CE:C2 Status=0

I tried to capture any thrown exception around the discoverServices() call, but the cursor never comes back to my code after that ch.receive() call.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/Beepiz/BleGattCoroutines/issues/57?email_source=notifications&email_token=ABVG6BJNCOAHNBMCI2BL5E3Q6H47DA5CNFSM4KIMUEWKYY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4IHAMVTQ, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABVG6BNNQ7JUSXUFYVTGTFDQ6H47DANCNFSM4KIMUEWA .

odahcam commented 4 years ago

Yes, it never reaches code after the discoverServices call. Couldn't it have a timeout?

LouisCAD commented 4 years ago

My suggestion is to try without the library, and see if you get the callback. If not, the issue is likely related to the BLE server you are using that never returns the services.

On Sat, Jan 18, 2020, 20:27 Luiz Machado notifications@github.com wrote:

Yes, it never reaches code after the discoverServices call.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/Beepiz/BleGattCoroutines/issues/57?email_source=notifications&email_token=ABVG6BJBY3XEWPOHLLOD6KDQ6NJ2XA5CNFSM4KIMUEWKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJKAFDY#issuecomment-575931023, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABVG6BPPMPRCF3KFZF4BKCLQ6NJ2XANCNFSM4KIMUEWA .

odahcam commented 4 years ago

It's already implemented without the library using the god callback style and it have been working for a while, that's because I think this is a problem with the lib itself. I was trying to migrate to this lib in order to improve performance and improve reliability.

LouisCAD commented 4 years ago

Then please look at the connection parameters and tell me if you spot a difference.

odahcam commented 4 years ago

Ok, so considering device.connectGatt call:

odahcam commented 4 years ago

You also asked me if I put some breakpoints after the discoverServices call:

image

None of them (after the call) get hit.

odahcam commented 4 years ago

I've tested with the appCtx within my service and had no problems with it.

LouisCAD commented 4 years ago

appCtx is not used under the hood anyway.

Regarding the breakpoints, I don't really understand the screenshot. It seems you're editing the library code and putting breakpoints there while it's designed to be used without modification in suspending functions (aka. coroutines), and you should put breakpoints (or plain old logs) after the discoverServices() suspend call on GattConnection is complete.

LouisCAD commented 4 years ago

The sample app uses discoverServices() successfully. Can you try the sample app on your device?

odahcam commented 4 years ago

About the screenshot, I really edited the library's code, but just to add that try-catch block to see if there was any exception, because no code after the method call gets hit by the debugger.

I couldn't make the sample app to run due to incompatible Gradle versions: image

And my Studio is unable to update it: image

I have no idea on how to manually update this.

LouisCAD commented 4 years ago

If there was an exception, it'd be thrown.

For the error, you can go into build.gradle.kts and update AGP (Android Gradle Plugin) to the required version (3.5.3).

On Mon, Jan 20, 2020, 22:34 Luiz Machado notifications@github.com wrote:

About the screenshot, I really edited the library's code, but just to add that try-catch block to see if there was any exception, because no code after the method call gets hit by the debugger.

I couldn't make the sample app to run due to incompatible Gradle versions: [image: image] https://user-images.githubusercontent.com/3942006/72759118-7c5ee500-3bb3-11ea-8c4c-0162cf1b62da.png

And my Studio is unable to update it: [image: image] https://user-images.githubusercontent.com/3942006/72759130-8680e380-3bb3-11ea-8e3d-876f4fe9582c.png

I have no idea on how to manually update this.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/Beepiz/BleGattCoroutines/issues/57?email_source=notifications&email_token=ABVG6BLFOZIEL7MRCI7PBRLQ6YKIDA5CNFSM4KIMUEWKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJN3VNI#issuecomment-576436917, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABVG6BOFS77Q6VNBNEUUVHDQ6YKIDANCNFSM4KIMUEWA .

odahcam commented 4 years ago

You sure? I see no gradle version in there.

LouisCAD commented 4 years ago

My bad, it's defined in buildSrc/build.gradle.kts

On Wed, Jan 22, 2020, 12:53 Luiz Machado notifications@github.com wrote:

You sure? I see no gradle version in there.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/Beepiz/BleGattCoroutines/issues/57?email_source=notifications&email_token=ABVG6BMVALLJRIAFVRVRCELQ7AXSTA5CNFSM4KIMUEWKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJTI3QQ#issuecomment-577146306, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABVG6BLSNKH54CYYXMM6XUTQ7AXSTANCNFSM4KIMUEWA .

odahcam commented 4 years ago

I've openned the app, accepted the permission requests and turnt on my bluetooth manually, so a dialog appeared on the bottom of my screen asking me for me to choose a device to pair. The dialog then says is impossible to pair with the device (which is no news) since the device is not meant to be paired so I tried with another device (which was already paired) and got a timeout, so I tried another celphone (this time with android one which is almost pure) with already connected BLE devices such as Mi Band 4 and got only timeouts. Am I doing something wrong?

LouisCAD commented 4 years ago

The dialog is shown by Samsung, it has nothing to do with BLE. It's about Bluetooth classic, which is a very different thing.

Did you try changing the mac address of the device the sample tries connecting to with the mac address of your BLE enabled device?

odahcam commented 4 years ago

The dialog is shown by Samsung, it has nothing to do with BLE. It's about Bluetooth classic, which is a very different thing.

Yep, aware.

Did you try changing the mac address of the device the sample tries connecting to with the mac address of your BLE enabled device?

Nope, I didn't know that. I'll try tomorrow.

odahcam commented 4 years ago

I did the test and the device connected successful, altought the only toast that appeared on the screen was the device's name as you can see here: https://imgur.com/a/e1FcwUB

LouisCAD commented 4 years ago

So the library is working fine, as expected.

The sample is very basic, but that's enough to inspired you how to implement what you need for your use case.

odahcam commented 4 years ago

Can you explain me how differently the sample app is discovering services from the way I'm doing? Because I have no problem connecting to the device in my app, but to discover services in it.

LouisCAD commented 4 years ago

I cannot, because I don't have your code, but the name of the device is printed only after discoverServices completes successfully (includes the callback being called), so you can use the sample as a prototyping starting point as you told me it's working as expected.

odahcam commented 4 years ago

Yep, I just found the extension methods on the sample (wonder why they're used as example on README and didn't came with the lib) and debbuged them successfully. The first thing I discovered is that my bluetooth service does make some interference with the library and I believe the problem may be something with splitties because of what it does (but it's only a shot in the dark).

I was using my own bluetooth service (that one made within the Android documentation as I told here) to scan for BLE devices and select one device for connecting. I just didn't used it this time and used my service passing my device's MAC address [hardcoded] to it instead of scanning. This time I got a KotlinNullPointerException which is better than nothing.

This exception is generated on line 329 of GattConnectionImpl.kt which has the following content: val response = ch.receive(). I don't know what to do from here.

LouisCAD commented 4 years ago

wonder why they're used as example on README and didn't came with the lib

Because you should decide how you deal with disconnections (retries, backoff delay, max attempts, alert user…). The extensions from the sample just log that it failed, which is good only for a sample.

The first thing I discovered is that my bluetooth service does make some interference with the library and I believe the problem may be something with splitties because of what it does (but it's only a shot in the dark)

If you have other code (or even another app) interacting with the same device, of course that can cause interference and unexpected behavior! So if you want it to work, you need to stop breaking it, and that means disabling the other code that tries to do BLE on the same device concurrently.

This exception is generated on line 329 of GattConnectionImpl.kt

What are the operations that you were doing before that happened? Please, reply with all the calls to the library your are making in order, and tell me which one didn't resume as you get the crash.

odahcam commented 4 years ago

Just that:

        deviceFor(address).useBasic { _, services ->
            // my code in here
        }

This is from the sample code, so you already know it:

https://github.com/Beepiz/BleGattCoroutines/blob/3a660829a476ea2727eecc392bad41c9b9f0246d/sample-common/src/androidMain/kotlin/com/beepiz/blegattcoroutines/sample/common/extensions/GattHelpers.kt#L21-L47

I then stop calling it and went with:

val conn = GattConnection(device)
conn.connect()
val services = conn.discoverServices()

And it worked just fine. Now I just need to find out how to scan devices not using my own service, I think some docs on it would help beginners like me.

Because you should decide how you deal with disconnections [...]

Just to have access to the code in a simple way would help beginners to visualize how it's done, things in README seems a little magic.

means disabling the other code that tries to do BLE on the same device concurrently.

AFAIK I wasn't doing that, but my lack of knowledge on bluetooth and Kotlin might have put me in a bad place here. To get rid of my old bluetooth service is one of my goals with this lib BTW.

odahcam commented 4 years ago

Now I just need to find out how to scan devices not using my own service, I think some docs on it would help beginners like me.

I did it.


The issue is (after almost 3 months) still happening but I didn't investigate to know whats going wrong anymore since I managed to call discoverServices in some point of my app that it just works.

odahcam commented 4 years ago

I noticed another place where the same issue happens: when trying to disable remote device characteristic.

While closing my device's connection, I try to disable notifications from the characteristic I use and, when I call the method, the app gets stuck on the gattRequest method from GattConnectionImpl. It looks like writeChannel (passed down to the method as the first parameter) is expected to return something that never receives on the ch.receive() call on line 333.

I don't know why yet, but I cannot write descriptors at this point.