PhilipsHue / flutter_reactive_ble

Flutter library that handles BLE operations for multiple devices.
https://developers.meethue.com/
Other
662 stars 326 forks source link

MTU Negotiation wrong exchange #816

Closed riccardocescon closed 9 months ago

riccardocescon commented 10 months ago

Describe the bug When i try to negotiate a MTU with my BLE device, i request for a value, on the log i can see that value is used, but then when negotiation completes the devices has negotiated a value higher than the one i have asked for. By reading the BLE communication with WireShark, i am able to see that the app asks for a MTU value that is not the one i have asked for, it seems to ask for the higher it can. This is the function that i am using

  @override
  Future<MtuData> requestMtu({
    required FlutterReactiveBle ble,
    required String deviceId,
    required int mtu,
  }) async {
    try {
      final mtuNegotiated = await ble.requestMtu(
        deviceId: deviceId,
        mtu: 123,
      );
      return Right(mtuNegotiated);
    } on Exception catch (e) {
      return Left(failures.MtuNegotiationFailure(e.toString()));
    }
  }

This is the log i am reading:

D/BluetoothGatt( 1464): configureMTU() - device: 68:B6:B3:96:F8:EE mtu: 123
D/BluetoothGatt( 1464): onConfigureMTU() - Device=68:B6:B3:96:F8:EE mtu=500 status=0
D/BluetoothGatt( 1464): onClientConnectionState() - status=19 clientIf=6 device=68:B6:B3:96:F8:EE
D/BluetoothGatt( 1464): close()

I can see that i am asking for an MTU of 123, as i wanted. But then the final MTU that i receive is 500, that i don't want to Here what i see with WireShark: image From here i see that the APP is asking for a MTU of size 517, and then the device response is for 500, so the final MTU is 500, but i just want the MTU of 123 as i input on the requestMTU function

To Reproduce Steps to reproduce the behavior:

  1. Connect to one device using connectTo() / connectToAdvertisingDevice()
  2. Read / Write / Subscribe to a protected / non-protected characteristic
  3. Request a lower MTU, such as 128 if you have BLE 5.0>=
  4. Watch the negotiated MTU result

Expected behavior I expected to ask for the MTU of 123, since the client BLE device has an MTU of 500 i expected to end up with a communication with 123 MTU, not 500

Smartphone / tablet

Peripheral device

remonh87 commented 10 months ago

This is not a bug in the library but it what actually happens is that you request an MTU with value x and the phone ble negotiates with the peripheral on this value. Apparently they couldn't honor your request and decided to go for 500 as a compromise (android default).

riccardocescon commented 10 months ago

This is not a bug in the library but it what actually happens is that you request an MTU with value x and the phone ble negotiates with the peripheral on this value. Apparently they couldn't honor your request and decided to go for 500 as a compromise (android default).

Thank you for your response! Shouldn't i be able to decide how much MTU i want to use in a comunication, or at Least, decide which Is my maximum in case i don't want ti limit It? If i go into debug mode on my custom ble device on the Gatt handler callback, i Always the MTU request for 247. Then i decided to ask for a MTU of 128 from flutter Just to see if i am receiving the right value on the device, but still i get 247. I then captured the packages to see what was happening i saw that the Android phone asked for 517, but i was expecting 128 instead. So, Is Android changing that value to am higher value Just because It sees It can be higher? Does he basically ignores my MTU request value? Again, thank you a lot for your hard work for this library!

riccardocescon commented 10 months ago

Hi @remonh87 , today i tested again with a different BLE peripheral device and found out that with both Android and iOS i am getting a weird value. I then used the nrf Connect app to connect to my BLE device, and i set the MTU to 123 and managed to get that value on my peripheral. Then i tried again to use my app, and when i am calling the function 'requestMTU(deviceId, mtu: 123)' the peripheral receives 500 (android default as you said) and not 123 as the function requested. So basically by using an external app and setting the MTU i am able to get the right value, meanwhile using this library i am always getting the default value and not the one passed to the function. Is it possible that there is an issue on the library? Again, thank you a lot for your effort on this package!

Update: still having this issue with the last release: 5.2.0

riccardocescon commented 10 months ago

Hi @remonh87 , i searched a bit and found out that the issue may be present on the connectionResult.rxConnection.requestMtu(size) call inside ReactiveBleClient.kt. I found this issue https://github.com/dariuszseweryn/RxAndroidBle/issues/843 that is not relevent to this one, but maybe there is something on the native librareis? could it be this the issue?

angelix commented 10 months ago

Your issue is related to Android 14 breaking changes, Keep in mind that it may even require a firmware update if the device is not handling the MTU negotiation correctly.

In our case, we had to perform a firmware update to set the maximum packet size to 512 bytes. Packages larger than this will be silently dropped by the operating system.

Google issue: https://issuetracker.google.com/issues/307234027

riccardocescon commented 10 months ago

Thank you a lot @angelix for your answer! I understood this issue caused by Android 14, but i am having this weire MTU Exchange with iOS too, even if i am requesting a smaller MTU, in the example above i have used 123 but from the wireshark capture we can see that Android 14 Is asking for 517 and not 123, isn't that the same issue since i am not requesting for AN higher value? I am developing the firmware for the peripherical, but i am never be able to get the requested MTU from the app, but i Always receive that 517 whatever i request for, isn't this a different issue?

angelix commented 10 months ago

Your peripheral device should respond to the Android device's MTU request with a reasonable value that can be accommodated by the peripheral. The final negotiated value will be a minimum of the Android requested value and the remote provided value (i.e. min(517, remoteMtu))

Even if you request a smaller MTU with requestMtu(), Android 14 is requesting 517. It's up to the device to respond with a reasonable value.

riccardocescon commented 9 months ago

Your peripheral device should respond to the Android device's MTU request with a reasonable value that can be accommodated by the peripheral. The final negotiated value will be a minimum of the Android requested value and the remote provided value (i.e. min(517, remoteMtu))

Even if you request a smaller MTU with requestMtu(), Android 14 is requesting 517. It's up to the device to respond with a reasonable value.

Thank you for your answer! I see the issue, but still i think there is something weird on the library. I have just tried with an Iphone 14 with iOS 17. I requested an MTU of 123 but the negotiation ended up with 244, this should not be possible, so i think there is an issue on this library too, because the final negotiated MTU cannot be higher than the one i have asked to right? Even if the Iphone supports 512 and i ask for 123, i should not have an MTU higher than the 123, at least i expect to have it equal or lower. Am i missing something or there is an actual issue on the requestMTU method on this package?

weliem commented 9 months ago

There is no method in CoreBluetooth to request a specific MTU. iOS always requests 517...or some other number like 248....depends on the iOS version

riccardocescon commented 9 months ago

There is no method in CoreBluetooth to request a specific MTU. iOS always requests 517...or some other number like 248....depends on the iOS version

I see, thank you!