flutter_reactive_ble icon indicating copy to clipboard operation
flutter_reactive_ble copied to clipboard

Error While Trying To Subscribe To Characteristic On iOS.

Open yuriboeira11tx opened this issue 2 years ago • 25 comments

Describe the bug I'm trying to subscribe to a feature, but an error is raised. Which is confusing, because days ago it was working correctly, without any problems.

To Reproduce Steps to reproduce the behavior:

  1. Connect to one / many devices using connectTo()
  2. Read/Write - ok
  3. Subscribe - error

Smartphone / tablet

  • Device: iPhone XS
  • OS: iOS 15.0
  • Package version: 4.0.1

Additional context Captura de Tela 2021-10-13 às 9 49 45 AM

yuriboeira11tx avatar Oct 13 '21 12:10 yuriboeira11tx

In the version 5.0.0 of package, error persist.

yuriboeira11tx avatar Oct 13 '21 12:10 yuriboeira11tx

In example app when click the Subscribe button this occurs in the log:

Captura de Tela 2021-10-13 às 11 08 36 AM

yuriboeira11tx avatar Oct 13 '21 14:10 yuriboeira11tx

@remonh87 had the same problem with the example app? Need help for this.

yuriboeira11tx avatar Nov 09 '21 19:11 yuriboeira11tx

@yuriboeira11tx does your characteristic support subscribing and also we reverted some change in 5.0.2 which can be worth to try out.

remonh87 avatar Nov 11 '21 07:11 remonh87

So, I decided to test this version yesterday via the example application. Unfortunately the error persists! On Android it's running beautifully, the problem is on iOS. It gives the same error when it gets to the subscribeToCharacteristic() line. I think it probably has to do with some system update as I'm testing both my app and the sample app and it's not working on iOS 15.0. I hope they can identify the problem. Does this error happen in your project?

yuriboeira11tx avatar Nov 11 '21 13:11 yuriboeira11tx

And yes, my feature has subscription support and I can prove why it works on Android (version 11).

yuriboeira11tx avatar Nov 11 '21 13:11 yuriboeira11tx

@yuriboeira11tx I encounter the same error on iOS and able to reproduce it with the Example app. I'm not sure if it's the same cause as the issue described in this ticket, but at least in my case, it looks like the device does not correctly specify the Client Characteristic Configuration Descriptor for custom notifiable characteristics. I found other issues described similar error condition:

  • https://github.com/PhilipsHue/flutter_reactive_ble/issues/188
  • https://github.com/xabre/xamarin-bluetooth-le/issues/191
  • https://github.com/PhilipsHue/flutter_reactive_ble/issues/309
  • https://github.com/randdusing/cordova-plugin-bluetoothle/issues/405

Even with the error, the subscription to custom characteristics should still work and the client should receive notification data. To test this, modify the plugin's Central.swift in the PeripheralDelegate:onCharacteristicNotificationStateUpdate to always return nil for error as follow:

onCharacteristicNotificationStateUpdate: papply(weak: self) { central, characteristic, error in
    central.characteristicNotifyRegistry.updateTask(
        key: QualifiedCharacteristic(characteristic),
        action: { $0.complete(error: nil) }      // <-- don't forward error for testing only
    )
}

Compile and run the Example app:

  • Select a notifiable characteristic
  • Select Subscribe
  • Select Read
  • The characteristic data should be displayed for both Read and Subscribe

@remonh87 This PR, https://github.com/PhilipsHue/flutter_reactive_ble/pull/193, seems to fix similar issue on Android. Do we need to have a similar PR for iOS? This is my naive implementation,

onCharacteristicNotificationStateUpdate: papply(weak: self) { central, characteristic, error in
    let err: Error? = {
        guard let nserror = error as NSError?
        else { return error }
        if (nserror.code == 10 && (characteristic.descriptors?.isEmpty ?? true) == true) { return nil }
        return error
    }()

    central.characteristicNotifyRegistry.updateTask(
        key: QualifiedCharacteristic(characteristic),
        action: { $0.complete(error: err) }
    )
}

k10dev avatar Nov 18 '21 17:11 k10dev

Thanks for the collaboration, I will ask my colleague who developed the firmware to check if he implemented descriptors. I'll be back with updates on the case. But one factor that shouldn't go unnoticed is that before the 15.0 version of iOS, it worked perfectly and even faster than Android. But at the moment, iOS is kind of inactive in my project.

yuriboeira11tx avatar Nov 18 '21 19:11 yuriboeira11tx

@werediver I noticed @k10dev did some interesting observations. Can you have a look at it?

remonh87 avatar Nov 18 '21 19:11 remonh87

@remonh87 @k10dev It's surprising that the subscription still works after an error is reported. If that's indeed acceptable to conditionally ignore that error, we should check not only the error code, but also the error domain, put the human readable error description in a comment and explain why that is needed.

werediver avatar Nov 19 '21 09:11 werediver

@remonh87, @k10dev @werediver Hey guys. I come to let you know that the problem has been fixed. There is no problem with the library, it is working perfectly on both platforms. The important information is that, it is necessary that the firmware has descriptors in its characteristics. Otherwise this exception is thrown and feature subscription will not work on iOS. Thanks for the clarification and support!

yuriboeira11tx avatar Nov 19 '21 18:11 yuriboeira11tx

I will leave this issue open and will add additional text to the error description. Good catch!!

remonh87 avatar Nov 19 '21 18:11 remonh87

@remonh87 @werediver We still would like to have a patch for backward compatibility to support devices in production that can't be updated with new firmware. We've made the fix in our firmware going forward.

There is another interesting observation. For characteristic with correct descriptors specified, but not have the BluetoothGattCharacteristic.PERMISSION_WRITE added to the characteristic.properties, we would receive this error instead Error Domain=CBATTErrorDomain Code=3 "Writing is not permitted.". Similar to the other error 10, the client would continue to receive the notification data. Perhaps the workaround should include a check for this as well

onCharacteristicNotificationStateUpdate: papply(weak: self) { central, characteristic, error in
    let err: Error? = {
        guard let nserror = error as NSError?
        else { return error }
        if (nserror.domain == "CBATTErrorDomain"
            && (nserror.code == 3
                || nserror.code == 10 && (characteristic.descriptors?.isEmpty ?? true) == true
            )) {
            return nil
        }
        return error
    }()

    central.characteristicNotifyRegistry.updateTask(
        key: QualifiedCharacteristic(characteristic),
        action: { $0.complete(error: err) }
    )
}

I did encounter an exception in the Flutter code in the callback on receiving this error

Error Domain=CBATTErrorDomain Code=1 "The handle is invalid." UserInfo={NSLocalizedDescription=The handle is invalid.}
onCharacteristicValueUpdate: papply(weak: self) { central, characteristic, error in
     onCharacteristicValueUpdate(central, QualifiedCharacteristic(characteristic), characteristic.value, error)
}

Stacktrace:

Runner[1790:899573] flutter: Exception: GenericFailure<CharacteristicValueUpdateError>(code: CharacteristicValueUpdateError.unknown, message: "Error Domain=CBATTErrorDomain Code=1 "The handle is invalid." UserInfo={NSLocalizedDescription=The handle is invalid.}")
Runner[1790:899573] flutter: #0      Result.dematerialize.<anonymous closure> (package:reactive_ble_platform_interface/src/model/result.dart:22:13)
#1      Result.iif (package:reactive_ble_platform_interface/src/model/result.dart:34:21)
#2      Result.dematerialize (package:reactive_ble_platform_interface/src/model/result.dart:15:28)
#3      ConnectedDeviceOperationImpl.readCharacteristic.<anonymous closure> (package:flutter_reactive_ble/src/connected_device_operation.dart:47:40)
#4      _MapStream._handleData (dart:async/stream_pipe.dart:213:31)
#5      _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:153:13)
#6      _rootRunUnary (dart:async/zone.dart:1436:47)
#7      _CustomZone.runUnary (dart:async/zone.dart:1335:19)
#8      _CustomZone.runUnaryGuarded (dart:async/zone.dart:1244:7)
#9      _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:341:11)
#10     _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271:7)
#11     _ForwardingStreamSubscription._add (dart:async/stream_pipe.dart:123:11)
#12     _WhereStream._handleData (dart:async/stream_pipe.dart:195:12)
#13     _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:153:13)
#14     _rootRunUnary (dart:async/zone.dart:1436:47)
#15     _CustomZone.runUnary (dart:async/zone.dart:1335:19)
#16     _CustomZone.runUnaryGuarded (dart:async/zone.dart:1244:7)
#17     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:341:11)
#18     _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271:7)
#19     _ForwardingStreamSubscription._add (dart:async/stream_pipe.dart:123:11)
#20     _MapStream._handleData (dart:async/stream_pipe.dart:218:10)
#21     _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:153:13)
#22     _rootRunUnary (dart:async/zone.dart:1436:47)
#23     _CustomZone.runUnary (dart:async/zone.dart:1335:19)
#24     _CustomZone.runUnaryGuarded (dart:async/zone.dart:1244:7)
#25     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:341:11)
#26     _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271:7)
#27     _ForwardingStreamSubscription._add (dart:async/stream_pipe.dart:123:11)
#28     _MapStream._handleData (dart:async/stream_pipe.dart:218:10)
#29     _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:153:13)
#30     _rootRunUnary (dart:async/zone.dart:1436:47)
#31     _CustomZone.runUnary (dart:async/zone.dart:1335:19)
#32     _CustomZone.runUnaryGuarded (dart:async/zone.dart:1244:7)
#33     CastStreamSubscription._onData (dart:_internal/async_cast.dart:85:11)
#34     _rootRunUnary (dart:async/zone.dart:1436:47)
#35     _CustomZone.runUnary (dart:async/zone.dart:1335:19)
#36     _CustomZone.runUnaryGuarded (dart:async/zone.dart:1244:7)
#37     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:341:11)
#38     _DelayedData.perform (dart:async/stream_impl.dart:591:14)
#39     _StreamImplEvents.handleNext (dart:async/stream_impl.dart:706:11)
#40     _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:663:7)
#41     _rootRun (dart:async/zone.dart:1420:47)
#42     _CustomZone.run (dart:async/zone.dart:1328:19)
#43     _CustomZone.runGuarded (dart:async/zone.dart:1236:7)
#44     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1276:23)
#45     _rootRun (dart:async/zone.dart:1428:13)
#46     _CustomZone.run (dart:async/zone.dart:1328:19)
#47     _CustomZone.runGuarded (dart:async/zone.dart:1236:7)
#48     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1276:23)
#49     _microtaskLoop (dart:async/schedule_microtask.dart:40:21)
#50     _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)

k10dev avatar Nov 19 '21 19:11 k10dev

@k10dev Are you absolutely sure your app doesn't attempt to write to that characteristic when you're getting "Writing is not permitted"?

werediver avatar Nov 22 '21 15:11 werediver

@werediver I actually used the Example app, and as soon as I select the Subscribe button, it would return the Writing is not permitted error.

k10dev avatar Nov 22 '21 16:11 k10dev

Hello, @werediver @k10dev @remonh87 I am also facing the same issue with this plugin. I am using the example provided in your repository and my app is working fine with Android phones.

Getting below error while calling subscribeToCharacteristic method.

flutter: Error unsubscribing from notifications: PlatformException(CBATTErrorDomain:10, The attribute could not be found., {NSLocalizedDescription: The attribute could not be found.}, null)
flutter: Error unsubscribing from notifications: PlatformException(CBATTErrorDomain:10, The attribute could not be found., {NSLocalizedDescription: The attribute could not be found.}, null)
flutter: Error unsubscribing from notifications: PlatformException(CBATTErrorDomain:10, The attribute could not be found., {NSLocalizedDescription: The attribute could not be found.}, null)
flutter: Error unsubscribing from notifications: PlatformException(CBATTErrorDomain:10, The attribute could not be found., {NSLocalizedDescription: The attribute could not be found.}, null)
flutter: Error unsubscribing from notifications: PlatformException(CBATTErrorDomain:10, The attribute could not be found., {NSLocalizedDescription: The attribute could not be found.}, null)
flutter: Error unsubscribing from notifications: PlatformException(CBATTErrorDomain:10, The attribute could not be found., {NSLocalizedDescription: The attribute could not be found.}, null)

Note: There is no issue with my firmware as I am able to get the data from characteristics using BLE Scanner App.

Hello, this morning i did some testing and found that there is now an exception throwing when i disconnect from my device.

flutter: Disconnecting to device: 5DD0594D-2562-AC72-EE0F-B066FE45C95C
flutter: Error unsubscribing from notifications: PlatformException(reactive_ble_mobile.Central.(unknown context at $10338f0e4).Failure:1, The operation couldn’t be completed. (reactive_ble_mobile.Central.(unknown context at $10338f0e4).Failure error 1.), {}, null)

yuriboeira11tx avatar Nov 25 '21 17:11 yuriboeira11tx

Hello, this morning I did some testing and found that there is now an exception throwing when i disconnect from my device.

flutter: Disconnecting to device: 5DD0594D-2562-AC72-EE0F-B066FE45C95C
flutter: Error unsubscribing from notifications: PlatformException(reactive_ble_mobile.Central.(unknown context at $10338f0e4).Failure:1, The operation couldn’t be completed. (reactive_ble_mobile.Central.(unknown context at $10338f0e4).Failure error 1.), {}, null)

@yuriboeira11tx I have also applied the same workaround which @k10dev mentioned in his comment and now facing the same issue as you.

@remonh87 @werediver

@dhaval-k-simformsolutions @yuriboeira11tx Are you using the Example app for testing? I only able to recreate the issue with the Example app when I press the Subscribe button multiple times. The exception is thrown in the Central:resolve function when there isn't an active peripheral for a given uuid,

private func resolve(connected peripheralID: PeripheralID) throws -> CBPeripheral {
    guard let peripheral = activePeripherals[peripheralID]
    else { throw Failure.peripheralIsUnknown(peripheralID) }    // <-- Error

    guard peripheral.state == .connected
    else { throw Failure.peripheralIsNotConnected(peripheralID) }

    return peripheral
}

This error is most likely caused by the code in the Example app by disconnecting the peripheral multiple times and not in the plugin.

k10dev avatar Nov 29 '21 16:11 k10dev

@k10dev Yes I am using the example app for the testing and I am getting the above exception while I perform the below steps in the example app.

  1. Connect to device
  2. Discover Services
  3. Subscribe under any characteristics and I am able to get the data
  4. Disconnect with device
  5. Discover services again(I got duplicate data this time for the service IDs - check this comment in issue #384 )

After connecting to the second time with the same device if I click on subscribe, Not get any data, and then if I click on the Disconnect in iOS getting the below exception:

688091db-1736-4179-b7ce-e42a724a6a68 Error unsubscribing from notifications: PlatformException(reactive_ble_mobile.PluginError:7, The operation couldn’t be completed. (reactive_ble_mobile.PluginError error 7.), {}, null)

Note: Facing a similar type of issue while disconnecting with the device and reconnecting and then trying on subscribe but the exception is slide different. #384 is already open for the same.

I think this is related to the issue where we get the duplicate data for the service ids while we reconnect with the same device, maybe both are connected somewhere.

I had the same error. I was able to fix it by canceling the BLE Characteristic Streams first, adding a delay of a few hundred milliseconds then canceling the ConnectionStateUpdate stream.

example

await characteristicStream?.cancel(); await Future.delayed(const Duration(milliseconds: 300)); await deviceConnectionStateStream?.cancel();

saho-ventures avatar Jan 16 '22 08:01 saho-ventures

https://github.com/PhilipsHue/flutter_reactive_ble/issues/586

MeteGUL avatar Jul 01 '22 07:07 MeteGUL

@yuriboeira11tx
@remonh87 I am having almost the same error and I tried @saho-ventures solution but did not help

I am getting the following error when I:

  1. Connect to a device
  2. Subscribe to a characteristic
  3. Disconnect from the device
  4. Connect again to the device
  5. Discover services

The error I get is:

Error occured when discovering services: PlatformException(reactive_ble_mobile.Central.(unknown context at $10390b160).Failure:2, The operation couldn’t be completed. (reactive_ble_mobile.Central.(unknown context at $10390b160).Failure error 2.), {}, null) PlatformException

Do you have more informations about the failure code 2 ? I tried to reproduce this in the example app but could not.

vladms avatar Jan 19 '23 15:01 vladms

Does Anybody found work around for the same as i am getting below exception.

PlatformException(reactive_ble_mobile.Central. (unknown context at $1047c6d94). Failure:5, The operation couldn't be completed. (reactive_ble_mobile Central (unknown context at $1047c6d94).Failure error 5.), {}, null)

Brinfotech1407 avatar Aug 09 '23 06:08 Brinfotech1407

I had the below error when I:

  1. connectToAdvertisingDevice
  2. subscribeToCharacteristic

flutter: PlatformException(reactive_ble_mobile.Central.(unknown context at $10269b160).Failure:5, The operation couldn’t be completed. (reactive_ble_mobile.Central.(unknown context at $10269b160).Failure error 5.), {}, null)

It turns out it was because my characteristic was not notifiable, I had to modify the firmware to make it notifiable

notifiable

I still however, get this error:

flutter: Error unsubscribing from notifications: PlatformException(reactive_ble_mobile.Central.(unknown context at $10269b160).Failure:5, The operation couldn’t be completed. (reactive_ble_mobile.Central.(unknown context at $10269b160).Failure error 5.), {}, null)

When I close the streams of

  1. connectToAdvertisingDevice
  2. subscribeToCharacteristic

But it doesn't seem to affect the functionality

omarica avatar Aug 19 '23 11:08 omarica