chipweinberger / flutter_blue_plus

Flutter plugin for connecting and communicationg with Bluetooth Low Energy devices, on Android, iOS, macOS
Other
781 stars 471 forks source link

[Help]: Services Changed Characteristic: `setNotifyValue(true)` returns `Subscribe Error: GATT_WRITE_NOT_PERMITTED` #711

Closed Bilal1025 closed 11 months ago

Bilal1025 commented 11 months ago

Requirements

Have you checked this problem on the example app?

Yes

FlutterBluePlus Version

1.29.6

Flutter Version

3.16.1

What OS?

Android

OS Version

ONE UI 5.1, Android - 13

Bluetooth Module

EFR32xG22 2.4 GHz 6 dBm Radio Board (BRD4182A Rev B06)

What is your problem?

Calling setNotifyValue(true) on characteristic returns Subscribe Error: GATT_WRITE_NOT_PERMITTED on 0x2A05 (Services Changed characteristic), it does have notify property enabled. I am using OTA functionality to flash firmware to hardware, but when in OTA mode discoverServices() crashes due to this.

Note: it works fine on kotlin/Java (using native android code)

Logs

I/ViewRootImpl@4f7c3b4[MainActivity](10698): ViewPostIme pointer 0
I/ViewRootImpl@4f7c3b4[MainActivity](10698): ViewPostIme pointer 1
I/flutter (10698): [FBP] <setNotifyValue> args: {remote_id: 38:5B:44:BB:0C:64, service_uuid: 1801, secondary_service_uuid: null, characteristic_uuid: 2a05, force_indications: false, enable: true}
D/[FBP-Android](10698): [FBP] onMethodCall: setNotifyValue
D/BluetoothGatt(10698): setCharacteristicNotification() - uuid: 00002a05-0000-1000-8000-00805f9b34fb enable: true
I/flutter (10698): [FBP] <setNotifyValue> result: true
I/flutter (10698): [FBP] <getAdapterState> args: null
D/[FBP-Android](10698): [FBP] onMethodCall: getAdapterState
I/flutter (10698): [FBP] <getAdapterState> result: {adapter_state: 4}
D/[FBP-Android](10698): [FBP] onDescriptorWrite: 2902 status: 3
I/flutter (10698): [FBP] [[ OnDescriptorWritten ]] result: {error_string: GATT_WRITE_NOT_PERMITTED, service_uuid: 1801, success: 0, remote_id: 38:5B:44:BB:0C:64, descriptor_uuid: 2902, error_code: 3, characteristic_uuid: 2a05, value: }
chipweinberger commented 11 months ago

2A05 (Services Changed Characteristic) may not support being subscribed to. You need to check first.

    if (Platform.isIOS == false && Platform.isMacOS == false) {
      BluetoothCharacteristic? c = _servicesChangedCharacteristic;
      if (c != null && (c.properties.notify || c.properties.indicate) && c.isNotifying == false) {
        await c.setNotifyValue(true);
      }
    }

But FBP already listens to the Services Changed characteristic!!

in FBP we call it onServicesReset (because you must call discover services again).

  /// Services Reset Stream
  ///  - uses the GAP Services Changed characteristic (0x2A05)
  ///  - you must re-call discoverServices()
  Stream<void> get onServicesReset 

Note: we listen to Services Changed characteristic in discoverServices()

    // in order to match iOS behavior on all platforms,
    // we always listen to the Services Changed characteristic if it exists.
    if (Platform.isIOS == false && Platform.isMacOS == false) {
      BluetoothCharacteristic? c = _servicesChangedCharacteristic;
      if (c != null && (c.properties.notify || c.properties.indicate) && c.isNotifying == false) {
        await c.setNotifyValue(true);
      }
    }
Bilal1025 commented 11 months ago

@chipweinberger I've checked/verified that the 2A05 support being subscribed to, however when calling setNotifyValue(true) on it, I am getting that particular exception thrown. Even when I call discoverServices it throws that exception. I see that we are checking

if (c != null && (c.properties.notify || c.properties.indicate) && c.isNotifying == false) {

So if it doesn't support subscribing it shouldn't get called in the first place. But it gets called and crashes on this await c.setNotifyValue(true); and throws that exception

chipweinberger commented 11 months ago

what happens on ios?

you might just need to catch the exception and ignore it

chipweinberger commented 11 months ago

"Example in Android: In an Android application, when you have a reference to a BluetoothGattDescriptor object, you can use the getPermissions() method to check its permissions. This method returns an integer, and you can use bitwise AND operations with flags like BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE to determine the permissions."

https://developer.android.com/reference/android/bluetooth/BluetoothGattDescriptor

looks like FBP needs to check these fields

chipweinberger commented 11 months ago

getPermissions() is not available on iOS

I think I'll add code to FBP to check for this error, and print a warning + ignore.

There is nothing we can do. Your device does not allow it.

Bilal1025 commented 11 months ago

@chipweinberger

what happens on ios?

I will check that on an iOS device and will inform you

you might just need to catch the exception and ignore it I've done something like this for now:

try {
await widget.device.discoverServices();
} catch (e) {}
var serviceList = widget.device.servicesList;

There is nothing we can do. Your device does not allow it.

I was replicating Kotlin code in flutter and can confirm that on the Kotlin app it successfully subscribes to Service Changed characteristic I haven't looked into the details on how it subscribes but can check that. But the device does allow subscribing to the characteristic.

I think I'll add code to FBP to check for this error, and print a warning + ignore.

I think that will help but ideally it should subscribe to the characteristic (not sure what the issue is here)

For reference this is the Kotlin code that I am following: https://github.com/SiliconLabs/EFRConnect-android

chipweinberger commented 11 months ago

yes you can still subscribe, but its the writing of the CCCD that causes the problem.

I imagine your kotlin code will have thr same issue if you update the CCCD. This is a native error.

chipweinberger commented 11 months ago

Thinking about it more, I'll leave the code as is. The user can catch the exception.