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

Android - observer.collect throws NoSuchElementException #43

Closed
axelinternet opened this issue Dec 23, 2020 · 15 comments · Fixed by #49
Closed

Android - observer.collect throws NoSuchElementException #43

axelinternet opened this issue Dec 23, 2020 · 15 comments · Fixed by #49
Assignees
Milestone

Comments

@axelinternet
Copy link

axelinternet commented Dec 23, 2020

I'm trying to read some data of a UART Service that provides a "write without response" and a "notify" characteristic. I can write to it fine using cable but Kable but get the following error when using observer.collect { ... } to subscribe to notifications.

java.util.NoSuchElementException: Collection contains no element matching the predicate.

The characteristic works fine to read using other apps and sends out data about once a second. As probably will be apparent from my sample code below I'm fairly new to Kotlin but as far as I understand after reading up and digging through the source code I can't see why this error is produced.

I looked at issue 38 but since I go through the steps to get the characteristic I get this error even if it is discovered. Or did i misunderstand the cause of that issue?

The characteristic and observer objects looks fine to me, what I can notice is that the

Sample code

bluetoothScope.launch {
            val peripheral: Peripheral? = getPeripheral() // Get Peripheral using Scanner()
            if (peripheral != null) {
                peripheral.connect()
                val characteristic = peripheral.services
                        ?.first { service -> service.serviceUuid == UUID.fromString(UART_SERVICE)}
                        ?.characteristics?.first { characteristic ->
                            characteristic.characteristicUuid == UUID.fromString(RX_CHARACTERISTIC_UUID)
                        }
                if (characteristic != null ) {
                    val observer = peripheral.observe(characteristic)
                                try {
                                    observer.collect { data ->
                                        Log.d(TAG, data)
                                    }
                                } catch (e: NoSuchElementException) {
                                    Log.d(TAG, "Error: $e")
                                }
                }
                peripheral.state?.collect { state ->
                    when (state) {
                        State.Connected -> {
                            Log.d(TAG, "Connected")
                        }
                        else -> Log.d(TAG, "Not Connected")
                    }
                }
            }

        }
    }

Scan result

DiscoveredService(serviceUuid=6e400001-b5a3-f393-e0a9-e50e24dcca9e, 
characteristics=[
    DiscoveredCharacteristic(
        serviceUuid=6e400001-b5a3-f393-e0a9-e50e24dcca9e,
        characteristicUuid=6e400003-b5a3-f393-e0a9-e50e24dcca9e, 
        descriptors=[]), 
    DiscoveredCharacteristic(
        serviceUuid=6e400001-b5a3-f393-e0a9-e50e24dcca9e, 
        characteristicUuid=6e400002-b5a3-f393-e0a9-e50e24dcca9e, 
        descriptors=[])]
)]
@twyatt
Copy link
Member

twyatt commented Dec 23, 2020

The characteristic lookup in the internal cache when using observe definitely needs some improvement (to make it more robust and more helpful when these issues occur). #38 should give you better hints of what went wrong, but in the mean time it does look like this is a bug in Kable.

Generally, characteristicOf will be a simpler means of configuring your characteristic variable, e.g.:

val characteristic = characteristicOf(
    service = UART_SERVICE,
    characteristic = RX_CHARACTERISTIC_UUID
)

...but your approach should also work, and definitely shouldn't result in the exception you're seeing since you are looking up from the list of actual services discovered.

What is the value of UART_SERVICE and RX_CHARACTERISTIC_UUID?

@axelinternet
Copy link
Author

Thanks for the quick response @twyatt. I started out using the characteristicOf approach when I got this issue but wanted to eliminate that I didn't go for a characteristic that wasn't available.

UART_SERVICE = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
RX_CHARACTERISTIC_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"

It's a fairly standard UART Service.

I haven't investigated further but I assumed that the error was caused here in acquire() in Observers?

Please let me know if I can help you troubleshoot in anyway.

@twyatt
Copy link
Member

twyatt commented Dec 23, 2020

Your issue is actually closely related to #38, in that I believe you're still being hit by the internal characteristic lookup failure within write or read, despite having retrieved the characteristic object from the discovered services.

Definitely a shortcoming in how the write and read functions retrieve the platform characteristic object:
The services property converts the list of services to List<DiscoveredService>, whereas DiscoveredService only holds UUID details about the service (the reference to the actual platform service object is not held, to ensure that library consumers don't hold on to/leak the platform's service/characteristic objects).

Unfortunately this means that a lookup (in Peripheral's internal service cache) is still performed even when manually finding the service/characteristic yourself (as you have done).

For Android, in the future, we may consider holding a weak reference in the services/characteristics provided by services property, making the lookup only necessary if the weak reference is lost.

In the mean time, I'll be sure and get #38 closed out soon and push a SNAPSHOT or get another version published. It will at least give you a clearer picture of what is going wrong. Additionally, I'll brainstorm other possible causes of the failure you're seeing.

I haven't investigated further but I assumed that the error was caused here in acquire() in Observers?

I suspect it is actually here (as described above):

val characteristics = services
.first { it.serviceUuid == characteristic.serviceUuid }
.characteristics
return characteristics
.first { it.characteristicUuid == characteristic.characteristicUuid }
.bluetoothRemoteGATTCharacteristic

Please let me know if I can help you troubleshoot in anyway.

Thanks for the help and issue report. I'll keep you posted.

@twyatt
Copy link
Member

twyatt commented Dec 28, 2020

To help us track down the issue, I've added basic logging (to the Android platform only, under DEBUG log level) to the service/characteristic lookup.

When you have a chance, can you try the following dependency and provide the lookup logs?

import java.net.URI

repositories {
    maven { url = URI("https://oss.sonatype.org/content/repositories/snapshots") }
}

dependencies {
    implementation("com.juul.kable:core:0.1.0-issue-43-SNAPSHOT")
}

Logs should look similar to:

Android logcat
2020-12-28 12:02:39.445 19509-19509/com.juul.sensortag D/SensorTag: connect
2020-12-28 12:02:39.449 19509-19509/com.juul.sensortag D/BluetoothGatt: connect() - device: 54:6C:0E:53:01:7D, auto: false
2020-12-28 12:02:39.449 19509-19509/com.juul.sensortag D/BluetoothGatt: registerApp()
2020-12-28 12:02:39.450 19509-19509/com.juul.sensortag D/BluetoothGatt: registerApp() - UUID=4d5bd5bd-4ea6-4314-956f-6a6aa281675a
2020-12-28 12:02:39.451 19509-19529/com.juul.sensortag D/BluetoothGatt: onClientRegistered() - status=0 clientIf=9
2020-12-28 12:02:39.696 19509-19509/com.juul.sensortag D/SensorTag: connect
2020-12-28 12:02:39.724 19509-19529/com.juul.sensortag D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=9 device=54:6C:0E:53:01:7D
2020-12-28 12:02:39.728 19509-19551/com.juul.sensortag D/BluetoothGatt: discoverServices() - device: 54:6C:0E:53:01:7D
2020-12-28 12:02:40.113 19509-19529/com.juul.sensortag D/BluetoothGatt: onConnectionUpdated() - Device=54:6C:0E:53:01:7D interval=6 latency=0 timeout=500 status=0
2020-12-28 12:02:41.391 19509-19529/com.juul.sensortag D/BluetoothGatt: onSearchComplete() = Device=54:6C:0E:53:01:7D Status=0
2020-12-28 12:02:41.400 19509-19551/com.juul.sensortag D/BluetoothGatt: readRssi() - device: 54:6C:0E:53:01:7D
2020-12-28 12:02:41.407 19509-19509/com.juul.sensortag I/SensorTag: Enabling gyro
2020-12-28 12:02:41.408 19509-19509/com.juul.sensortag D/Kable: Looking up LazyCharacteristic(serviceUuid=f000aa80-0451-4000-b000-000000000000, characteristicUuid=f000aa82-0451-4000-b000-000000000000)
2020-12-28 12:02:41.408 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 00001800-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.409 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 00001801-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.409 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 0000180a-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.409 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 0000180f-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.410 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa00-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.410 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa20-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.410 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa40-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.410 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa80-0451-4000-b000-000000000000 -> true
2020-12-28 12:02:41.411 19509-19509/com.juul.sensortag D/Kable: Lookup characteristic: f000aa82-0451-4000-b000-000000000000 == f000aa81-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.411 19509-19509/com.juul.sensortag D/Kable: Lookup characteristic: f000aa82-0451-4000-b000-000000000000 == f000aa82-0451-4000-b000-000000000000 -> true
2020-12-28 12:02:41.412 19509-19509/com.juul.sensortag I/SensorTag: Enabling gyro
2020-12-28 12:02:41.412 19509-19509/com.juul.sensortag D/Kable: Looking up LazyCharacteristic(serviceUuid=f000aa80-0451-4000-b000-000000000000, characteristicUuid=f000aa82-0451-4000-b000-000000000000)
2020-12-28 12:02:41.412 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 00001800-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.413 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 00001801-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.413 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 0000180a-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.413 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 0000180f-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.413 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa00-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.413 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa20-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.413 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa40-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.414 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa80-0451-4000-b000-000000000000 -> true
2020-12-28 12:02:41.414 19509-19509/com.juul.sensortag D/Kable: Lookup characteristic: f000aa82-0451-4000-b000-000000000000 == f000aa81-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.414 19509-19509/com.juul.sensortag D/Kable: Lookup characteristic: f000aa82-0451-4000-b000-000000000000 == f000aa82-0451-4000-b000-000000000000 -> true
2020-12-28 12:02:41.417 19509-19509/com.juul.sensortag D/Kable: Looking up LazyCharacteristic(serviceUuid=f000aa80-0451-4000-b000-000000000000, characteristicUuid=f000aa81-0451-4000-b000-000000000000)
2020-12-28 12:02:41.417 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 00001800-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.417 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 00001801-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.418 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 0000180a-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.418 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 0000180f-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.418 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa00-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.418 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa20-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.418 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa40-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.418 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa80-0451-4000-b000-000000000000 -> true
2020-12-28 12:02:41.419 19509-19509/com.juul.sensortag D/Kable: Lookup characteristic: f000aa81-0451-4000-b000-000000000000 == f000aa81-0451-4000-b000-000000000000 -> true
2020-12-28 12:02:41.419 19509-19509/com.juul.sensortag D/BluetoothGatt: setCharacteristicNotification() - uuid: f000aa81-0451-4000-b000-000000000000 enable: true
2020-12-28 12:02:41.424 19509-19509/com.juul.sensortag D/SensorTag: RSSI: -50
2020-12-28 12:02:41.430 19509-19509/com.juul.sensortag I/SensorTag: Gyro enabled
2020-12-28 12:02:41.431 19509-19509/com.juul.sensortag V/SensorTag: period = 100.0
2020-12-28 12:02:41.431 19509-19509/com.juul.sensortag I/SensorTag: movement → writePeriod → data = 10 (0A)
2020-12-28 12:02:41.431 19509-19509/com.juul.sensortag D/Kable: Looking up LazyCharacteristic(serviceUuid=f000aa80-0451-4000-b000-000000000000, characteristicUuid=f000aa83-0451-4000-b000-000000000000)
2020-12-28 12:02:41.431 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 00001800-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.431 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 00001801-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.431 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 0000180a-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.431 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 0000180f-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.432 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa00-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.432 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa20-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.432 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa40-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.432 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa80-0451-4000-b000-000000000000 -> true
2020-12-28 12:02:41.432 19509-19509/com.juul.sensortag D/Kable: Lookup characteristic: f000aa83-0451-4000-b000-000000000000 == f000aa81-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.432 19509-19509/com.juul.sensortag D/Kable: Lookup characteristic: f000aa83-0451-4000-b000-000000000000 == f000aa82-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.432 19509-19509/com.juul.sensortag D/Kable: Lookup characteristic: f000aa83-0451-4000-b000-000000000000 == f000aa83-0451-4000-b000-000000000000 -> true
2020-12-28 12:02:41.435 19509-19509/com.juul.sensortag I/SensorTag: Gyro enabled
2020-12-28 12:02:41.435 19509-19509/com.juul.sensortag V/SensorTag: period = 100.0
2020-12-28 12:02:41.435 19509-19509/com.juul.sensortag I/SensorTag: movement → writePeriod → data = 10 (0A)
2020-12-28 12:02:41.435 19509-19509/com.juul.sensortag D/Kable: Looking up LazyCharacteristic(serviceUuid=f000aa80-0451-4000-b000-000000000000, characteristicUuid=f000aa83-0451-4000-b000-000000000000)
2020-12-28 12:02:41.435 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 00001800-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.436 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 00001801-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.436 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 0000180a-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.436 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == 0000180f-0000-1000-8000-00805f9b34fb -> false
2020-12-28 12:02:41.436 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa00-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.436 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa20-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.436 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa40-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.436 19509-19509/com.juul.sensortag D/Kable: Lookup service: f000aa80-0451-4000-b000-000000000000 == f000aa80-0451-4000-b000-000000000000 -> true
2020-12-28 12:02:41.436 19509-19509/com.juul.sensortag D/Kable: Lookup characteristic: f000aa83-0451-4000-b000-000000000000 == f000aa81-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.436 19509-19509/com.juul.sensortag D/Kable: Lookup characteristic: f000aa83-0451-4000-b000-000000000000 == f000aa82-0451-4000-b000-000000000000 -> false
2020-12-28 12:02:41.436 19509-19509/com.juul.sensortag D/Kable: Lookup characteristic: f000aa83-0451-4000-b000-000000000000 == f000aa83-0451-4000-b000-000000000000 -> true
2020-12-28 12:02:41.465 19509-19529/com.juul.sensortag D/BluetoothGatt: onConnectionUpdated() - Device=54:6C:0E:53:01:7D interval=36 latency=0 timeout=500 status=0
2020-12-28 12:02:41.526 19509-19509/com.juul.sensortag I/SensorTag: writeGyroPeriod complete
2020-12-28 12:02:41.531 19509-19509/com.juul.sensortag I/SensorTag: writeGyroPeriod complete
2020-12-28 12:02:42.429 19509-19551/com.juul.sensortag D/BluetoothGatt: readRssi() - device: 54:6C:0E:53:01:7D
2020-12-28 12:02:42.439 19509-19509/com.juul.sensortag D/SensorTag: RSSI: -49

Thanks!

@axelinternet
Copy link
Author

Switched over to using characteristicOf() and added the snapshot. One additional note is that the device that I'm using does not advertise it's services until connected to. If the services are cached when scanning that could maybe be a potential culprit. Can't see anything funky in these logs though.

Log

2020-12-29 11:30:37.142 10640-11783/com.ownership D/Btle: -> Found the UART advertisement
2020-12-29 11:30:37.161 10640-11784/com.ownership D/BluetoothGatt: connect() - device: ED:67:38:7C:7E:90, auto: false
2020-12-29 11:30:37.161 10640-11784/com.ownership D/BluetoothGatt: registerApp()
2020-12-29 11:30:37.161 10640-11784/com.ownership D/BluetoothGatt: registerApp() - UUID=c8bd667b-0c52-498c-a912-db34c5d3c983
2020-12-29 11:30:37.164 10640-11810/com.ownership D/BluetoothGatt: onClientRegistered() - status=0 clientIf=13
2020-12-29 11:30:42.270 10640-11699/com.ownership D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=13 device=ED:67:38:7C:7E:90
2020-12-29 11:30:42.283 10640-11811/com.ownership D/BluetoothGatt: discoverServices() - device: ED:67:38:7C:7E:90
2020-12-29 11:30:42.821 10640-11863/com.ownership D/BluetoothGatt: onConnectionUpdated() - Device=ED:67:38:7C:7E:90 interval=6 latency=0 timeout=500 status=0
2020-12-29 11:30:43.593 10640-11699/com.ownership D/BluetoothGatt: onSearchComplete() = Device=ED:67:38:7C:7E:90 Status=0
2020-12-29 11:30:43.621 10640-11782/com.ownership D/Kable: Looking up LazyCharacteristic(serviceUuid=6e400001-b5a3-f393-e0a9-e50e24dcca9e, characteristicUuid=6e400003-b5a3-f393-e0a9-e50e24dcca9e)
2020-12-29 11:30:43.622 10640-11782/com.ownership D/Kable: Lookup service: 6e400001-b5a3-f393-e0a9-e50e24dcca9e == 00001800-0000-1000-8000-00805f9b34fb -> false
2020-12-29 11:30:43.622 10640-11782/com.ownership D/Kable: Lookup service: 6e400001-b5a3-f393-e0a9-e50e24dcca9e == 0000180a-0000-1000-8000-00805f9b34fb -> false
2020-12-29 11:30:43.622 10640-11782/com.ownership D/Kable: Lookup service: 6e400001-b5a3-f393-e0a9-e50e24dcca9e == 6e400001-b5a3-f393-e0a9-e50e24dcca9e -> true
2020-12-29 11:30:43.622 10640-11782/com.ownership D/Kable: Lookup characteristic: 6e400003-b5a3-f393-e0a9-e50e24dcca9e == 6e400003-b5a3-f393-e0a9-e50e24dcca9e -> true
2020-12-29 11:30:43.622 10640-11782/com.ownership D/BluetoothGatt: setCharacteristicNotification() - uuid: 6e400003-b5a3-f393-e0a9-e50e24dcca9e enable: true
2020-12-29 11:30:43.624 10640-11782/com.ownership D/Btle: Did not find characteristic. java.util.NoSuchElementException: Collection contains no element matching the predicate.
2020-12-29 11:30:43.672 10640-11699/com.ownership D/BluetoothGatt: onConnectionUpdated() - Device=ED:67:38:7C:7E:90 interval=36 latency=0 timeout=500 status=0
2020-12-29 11:30:58.928 10640-11863/com.ownership D/BluetoothGatt: onClientConnectionState() - status=8 clientIf=13 device=ED:67:38:7C:7E:90
2020-12-29 11:30:58.930 10640-11811/com.ownership D/BluetoothGatt: close()
2020-12-29 11:30:58.937 10640-11811/com.ownership D/BluetoothGatt: unregisterApp() - mClientIf=13
2020-12-29 11:30:58.940 10640-11811/com.ownership D/BluetoothGatt: close()
2020-12-29 11:30:58.946 10640-11811/com.ownership D/BluetoothGatt: unregisterApp() - mClientIf=0

@twyatt
Copy link
Member

twyatt commented Dec 29, 2020

Switched over to using characteristicOf() and added the snapshot.

Do you mind pasting your updated sample code that is producing these logs?

One additional note is that the device that I'm using does not advertise it's services until connected to. If the services are cached when scanning that could maybe be a potential culprit.

That shouldn't be an issue. The services/characteristics are cached just after service discovery (which Kable does automatically upon connection).

Can't see anything funky in these logs though.

Ya, logs look reasonable. The lookup appears to succeed, so it's quite bizarre that you're getting the NoSuchElementException. Hoping seeing your updated sample code might help give more hints or trigger some idea.


Thanks for your help debugging this issue!

@m31wayfarer
Copy link

m31wayfarer commented Dec 29, 2020

I have reproduced similar crash with SensorTag example when disconnect peripheral in during of connecting. You can reproduce this with my peripheral app attached here
peripheral.apk.zip

java.util.NoSuchElementException: Collection contains no element matching the predicate. at com.juul.kable.PeripheralKt.bluetoothGattCharacteristicFrom(Peripheral.kt:269) at com.juul.kable.PeripheralKt.access$bluetoothGattCharacteristicFrom(Peripheral.kt:1) at com.juul.kable.AndroidPeripheral.startNotifications$core_debug(Peripheral.kt:193) at com.juul.kable.Observers$acquire$1.invokeSuspend(Observers.kt:45) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:342) at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith$default(DispatchedContinuation.kt:263) at kotlinx.coroutines.internal.ScopeCoroutine.afterCompletion(Scopes.kt:27) at kotlinx.coroutines.JobSupport.continueCompleting(JobSupport.kt:933) at kotlinx.coroutines.JobSupport.access$continueCompleting(JobSupport.kt:28) at kotlinx.coroutines.JobSupport$ChildCompletion.invoke(JobSupport.kt:1152) at kotlinx.coroutines.JobSupport.notifyCompletion(JobSupport.kt:1520) at kotlinx.coroutines.JobSupport.completeStateFinalization(JobSupport.kt:323) at kotlinx.coroutines.JobSupport.finalizeFinishingState(JobSupport.kt:240) at kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath(JobSupport.kt:903) at kotlinx.coroutines.JobSupport.tryMakeCompleting(JobSupport.kt:860) at kotlinx.coroutines.JobSupport.makeCompletingOnce$kotlinx_coroutines_core(JobSupport.kt:825) at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:111) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:226) at android.os.Handler.handleCallback(Handler.java:873) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7156) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:975)

@axelinternet
Copy link
Author

Very similar to what I posted previously

bluetoothScope.launch {
            val peripheral: Peripheral? = getPeripheral() // Get Peripheral using Scanner()
            if (peripheral != null) {
                peripheral.connect()
                val characteristic = characteristicOf(
                    service = UART_SERVICE,
                    characteristic = RX_CHARACTERISTIC_UUID
                )
                if (characteristic != null ) {
                    val observer = peripheral.observe(characteristic)
                          try {
                              observer.collect { data ->
                                  Log.d(TAG, data)
                              }
                          } catch (e: NoSuchElementException) {
                              Log.d(TAG, "Did not find characteristic. $e")
                          }
                }
            }
        }
    }
    
private suspend fun getPeripheral(): Peripheral? {
      return try {
          val advertisement = Scanner()
                  .advertisements
                  .firstOrNull { advertisement ->
                      advertisement.name?.startsWith(nameofPeripheral) ?: false
                  }
          if (advertisement != null) {
              Log.d(TAG, "-> Found the UART advertisement")
              bluetoothScope.peripheral(advertisement)
          } else {
              null
          }
      } catch (e: Throwable) {
          Log.e(TAG, "Problem -> $e")
          return null
      }
  }

@twyatt
Copy link
Member

twyatt commented Dec 29, 2020

@axelinternet is the peripheral that is causing the crash publicly available? If so, I'll probably purchase one to diagnose the issue.

@axelinternet
Copy link
Author

No unfortunately not. I can see if I get the same error if I emulate it using some different hardware. I've built other apps that interface it fine (but never tried using android).

@twyatt
Copy link
Member

twyatt commented Dec 29, 2020

I can see if I get the same error if I emulate it using some different hardware.

Great, thanks! In the mean time I'll also start integrating a way to toggle detailed logging in Kable, to help track down issues like this.

@twyatt
Copy link
Member

twyatt commented Dec 29, 2020

@axelinternet just to make sure I'm not going down the wrong rabbit hole: could you provide the stacktrace?

So where you do:

} catch (e: NoSuchElementException) {
    Log.d(TAG, "Did not find characteristic. $e")
}

Change it to something like:

} catch (e: NoSuchElementException) {
    Log.d(TAG, "Did not find characteristic. $e", e)

    // I can't quite remember if `Log.d` prints the full trace, if not, this may be needed instead of adding 3rd parameter to `Log.d`
    e.printStackTrace()
}

@twyatt
Copy link
Member

twyatt commented Dec 29, 2020

Oh! I think I've spotted the issue. Somewhat shot in the dark but I think #42 (when fixed) will also fix your issue.

@axelinternet In your original issue report, you pasted your characteristic details:

DiscoveredService(serviceUuid=6e400001-b5a3-f393-e0a9-e50e24dcca9e, 
characteristics=[
    DiscoveredCharacteristic(
        serviceUuid=6e400001-b5a3-f393-e0a9-e50e24dcca9e,
        characteristicUuid=6e400003-b5a3-f393-e0a9-e50e24dcca9e, 
        descriptors=[]), 
    DiscoveredCharacteristic(
        serviceUuid=6e400001-b5a3-f393-e0a9-e50e24dcca9e, 
        characteristicUuid=6e400002-b5a3-f393-e0a9-e50e24dcca9e, 
        descriptors=[])]
)]

It appears that the characteristic that you're observing does not have the descriptor to enable observation. As pointed out in #42, "It's a violation of BLE specification" but is common enough that Kable should provide a workaround.

I should be able to get #42 fixed soon and you can give that a try (disabling writing to descriptor when observing).

@axelinternet
Copy link
Author

Ah that is true. I didn't think that this was required. There is a lot of "may" and "can" when googling around descriptors but just as said in #42 turns out that this is required if the characteristic is set to notify.

@twyatt twyatt self-assigned this Dec 30, 2020
@twyatt

This comment has been minimized.

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

Successfully merging a pull request may close this issue.

3 participants