Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
odahcam opened this issue Jan 17, 2020 · 26 comments
Labels

Comments

@odahcam
Copy link

odahcam commented Jan 17, 2020

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.

@odahcam odahcam changed the title Receivei channel poll fails when dicoverServices is called and keeps app stuck Receive channel's poll fails when dicoverServices is called and keeps app stuck Jan 17, 2020
@LouisCAD
Copy link
Collaborator

LouisCAD commented Jan 18, 2020 via email

@odahcam
Copy link
Author

odahcam commented Jan 18, 2020

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

@LouisCAD
Copy link
Collaborator

LouisCAD commented Jan 18, 2020 via email

@odahcam
Copy link
Author

odahcam commented Jan 18, 2020

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
Copy link
Collaborator

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

@odahcam
Copy link
Author

odahcam commented Jan 20, 2020

Ok, so considering device.connectGatt call:

  • appCtx: The app context is obviusly different, you use a function to get it and I simply pass my service's context.
  • autoConnect: is false, both the same.
  • callback: different, of course.
  • transport: I don't pass this parameter.
  • phy: I don't pass this parameter.

@odahcam
Copy link
Author

odahcam commented Jan 20, 2020

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

image

None of them (after the call) get hit.

@odahcam
Copy link
Author

odahcam commented Jan 20, 2020

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

@LouisCAD
Copy link
Collaborator

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
Copy link
Collaborator

LouisCAD commented Jan 20, 2020

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

@odahcam
Copy link
Author

odahcam commented Jan 20, 2020

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
Copy link
Collaborator

LouisCAD commented Jan 20, 2020 via email

@odahcam
Copy link
Author

odahcam commented Jan 22, 2020

You sure? I see no gradle version in there.

@LouisCAD
Copy link
Collaborator

LouisCAD commented Jan 22, 2020 via email

@odahcam
Copy link
Author

odahcam commented Jan 22, 2020

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
Copy link
Collaborator

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
Copy link
Author

odahcam commented Jan 22, 2020

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
Copy link
Author

odahcam commented Jan 23, 2020

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
Copy link
Collaborator

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
Copy link
Author

odahcam commented Jan 23, 2020

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
Copy link
Collaborator

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
Copy link
Author

odahcam commented Jan 24, 2020

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
Copy link
Collaborator

LouisCAD commented Jan 24, 2020

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
Copy link
Author

odahcam commented Jan 24, 2020

Just that:

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

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

suspend inline fun BluetoothDevice.useBasic(
connectionTimeoutInMillis: Long = 5000L,
block: (GattConnection, List<BluetoothGattService>) -> Unit
) {
val deviceConnection = GattConnection(this)
try {
deviceConnection.logConnectionChanges()
withTimeout(connectionTimeoutInMillis) {
deviceConnection.connect()
}
Timber.i("Connected!")
val services = deviceConnection.discoverServices()
Timber.i("Services discovered!")
block(deviceConnection, services)
} catch (e: TimeoutCancellationException) {
Timber.e("Connection timed out after $connectionTimeoutInMillis milliseconds!".also {
toast(it)
})
throw e
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
Timber.e(e)
} finally {
deviceConnection.close()
Timber.i("Closed!")
}

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
Copy link
Author

odahcam commented Mar 9, 2020

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
Copy link
Author

odahcam commented Mar 12, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants