NordicSemiconductor / Android-BLE-Library

A library that makes working with Bluetooth LE on Android a pleasure. Seriously.
BSD 3-Clause "New" or "Revised" License
1.99k stars 414 forks source link

onDescriptorWrite error 1 #481

Closed dononcharles closed 1 year ago

dononcharles commented 1 year ago

Hi, Please can you help me understand this error when i'm trying to enable notification on TX characteristic?


priority: 3; message:[Callback] Connection state changed with status: 0 and new state: 2 (CONNECTED)
priority: 4; message:Connected to 18:C2:93:00:48:AA
priority: 3; message:wait(1600)
E  priority: 2; message:Discovering services...
E  priority: 3; message:gatt.discoverServices()
D  discoverServices() - device: 18:C2:93:00:48:AA
D  onSearchComplete() = Device=18:C2:93:00:48:AA Status=0
E  priority: 4; message:Services discovered
E  priority: 2; message:Primary service found
E  priority: 3; message:gatt.setCharacteristicNotification(569a2000-b87f-490c-92cb-11ba5ea5167c, true)
D  setCharacteristicNotification() - uuid: 569a2000-b87f-490c-92cb-11ba5ea5167c enable: true
E  priority: 2; message:Enabling notifications for 569a2000-b87f-490c-92cb-11ba5ea5167c
E  priority: 3; message:gatt.writeDescriptor(00002902-0000-1000-8000-00805f9b34fb, value=0x01-00)
I  Controller GATT services discovered
E  onDescriptorWrite error 1
E  priority: 6; message:Error (0x1): GATT INVALID HANDLE
E  Could not subscribe to 18:C2:93:00:48:AA, status: 1
E  priority: 2; message:Disconnecting...
E  priority: 3; message:gatt.disconnect()
philips77 commented 1 year ago

Try connecting to the device using nRF Connect and select Refresh services from the top-menu. Perhaps your phone has cached previous services of the peripheral. Then try again.

dononcharles commented 1 year ago

@philips77 thanks for your tips, but does not help me. If i remove the device from the phone bonding table, i start the connection with pairing. After the pairing, i can connect to the BT module, 2nd times too, but from the 3rd times, i could not and the error above is shown. I would like to understand what can block the Gatt like this.

philips77 commented 1 year ago

Are you nullifying references to your characteristics and descriptors in onServicesInvalidated() and seeing them again in isRequiredServiceFound(...)?

Could you paste here relevant part of your BLE manager?

dononcharles commented 1 year ago

Hi @philips77 please have a look below:

override fun onServicesInvalidated() {  
    rxCharacteristic = null
    txCharacteristic = null
    modemInCharacteristic = null
    modemOutCharacteristic = null
}

   override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean {
    var writeRequest = false

        gatt.getService(LAIRD_VPS_SERVICE_UUID)?.run {
            rxCharacteristic = getCharacteristic(rxUUID)
            txCharacteristic = getCharacteristic(txUUID)
            modemInCharacteristic = getCharacteristic(modemInUUID)
            modemOutCharacteristic = getCharacteristic(modemOutUUID)
            writeRequest = (txCharacteristic?.properties ?: 0) > 0 && (modemInCharacteristic?.properties ?: 0) > 0 && (modemOutCharacteristic?.properties ?: 0) > 0
        }

    return  rxCharacteristic != null && txCharacteristic != null && writeRequest
}

  override fun initialize() {

        setNotificationCallback(txCharacteristic).with(txCallback)

        beginAtomicRequestQueue()
            .add(
                enableNotifications(txCharacteristic).fail { device, status: Int ->
                    Logger.error("Could not subscribe to $device, status: $status")
                    disconnect().enqueue()
                })
            .done { device: BluetoothDevice? ->
                Logger.info("Target: $device is initialized")
            }.enqueue()
}
dononcharles commented 1 year ago

Hi @philips77 i found that clearing the cache after every gatt.close() solve the problem. But it's a solution i do not really like.

override fun shouldClearCacheWhenDisconnected(): Boolean {
   Log.i(TAG, "fooo:shouldClearCacheWhenDisconnected")
   return true
}
philips77 commented 1 year ago

Does your device change services at some point? If so, is it sending Service Changed indication either after the change or after next reconnection?

philips77 commented 1 year ago

You may also use refreshServices().enqueued(), but don't think that will work on the initialize(..) as it would run in an endless loop.

dononcharles commented 1 year ago

Does your device change services at some point? If so, is it sending Service Changed indication either after the change or after next reconnection?

how can my device change services? i do not think about something like this. How can i know that?

I've also seen that with nRF Connect App, i can not even enable or disable the notification state on the characteristics. I need to 'click on refresh services' as you said in your first comment, before it works.

Before i used the clear cache function, if i disconnect from the server gatt and i restart the BT module on my "controller" than the connection work as intended.

philips77 commented 1 year ago

Then it looks like the device changes services, or at least handle numbers. Are you the author of the firmware?

A Bluetooth LE device as an Attribute Table with Services, Characteristics and Descriptors. Each attribute and value has a Handle number associated (UInt16). When you write/read an attribute/value, you send the handle number, not the full UUID (although there are options to read/write by UUID, but they are not supported on mobiles). A device may change its table at any time. For example, in Nordic nRF5 SDK when a device enters DFU mode it changes services to expose the DFU service, do the update and then runs again in the app mode with old (or new) services. Sometimes for the DFU mode the device increments its MAC address, so from the phone point of view it's a new device, not the old with changed table. Anyway, if it changes services it should indicate the change to the client. It the device is bonded it shall use Service Changed characteristic by sending an indication. The client should invalidate its cache and do service discovery again. On Android's native API you should get onServiceChanged callback (API 31+). Very often the change happens when the device restarts, so the indication is sent immediately after enabling encryption after connection. That's why in the BLE library it is possible to delay service discovery to make sure the indication is handled (otherwise Android would return cached services): https://github.com/NordicSemiconductor/Android-BLE-Library/blob/107435fcfcbe48ed26f4e550c033842cff6e5d3a/ble/src/main/java/no/nordicsemi/android/ble/BleManager.java#L617-L648 For non-bonded devices it's a bit easier. If the Service Change characteristic is present in the Attribute Table of a device it means the the client shall not cache services at all and shall do service discovery after each connection. If the Generic Attribute service does not have this characteristic a client is free to cache services and never clear them as this indicates that the device will never ever change services, e.g. does not support update, etc. And iOS respects that. Android, on the other hand, always caches on some versions, never caches on some other versions, etc. Hard to say. But there's a hidden API to clear the cache, which the BLE library allows by either returning true from shouldClearCacheWhenDisconnected(), or executing refreshDeviceCache request.

If the service change you're describing is not intended, try a different phone (perhaps there's some issue with your phone?) and check your firmware.

dononcharles commented 1 year ago

@philips77 , thanks for this detailed explanations. Am not the author of the fw. It bt (Lyra P) module bought from LAIRD company. I've tried Android 11, 12, 13 same issus, but with Huawey Android 10, i've not seen this issue. Also as info: if we do not enable the bonding config on the Bt module, everything works as well. The problem happens only when we've activated the bonding configuration

philips77 commented 1 year ago

Anyway, if the device does change service you may need to use you solution from here: https://github.com/NordicSemiconductor/Android-BLE-Library/issues/481#issuecomment-1479812561

dononcharles commented 1 year ago

@philips77, please i need your help for one more thing: my team is asking me to report this behavior to the owner of the BT module. What kind of question can i ask them? Do you think the can help us on this issue? or i need to figure out myself solution? Thanks

philips77 commented 1 year ago

What module or chip is used it doesn't matter. It depends on the firmware programmed on it. And this behavior may be intended. I have no idea what fw is that.

dononcharles commented 1 year ago

Thanks so much for yr time. You can close this issue.