11

Issue: After setting MTU>23 on Samsung Device (S10e) running Android 10 and writing data to a characteristic the connection times-out and is closed.

Root cause: The reason for the timeout is the phone hardware is not actually sending anything...so the end device does not respond(ack).

How do I know it’s not an end device issue: This problem does not occur with other devices (pixel) and is also doesn’t occur on the S10 if it’s running Android 9.

Details: We are using requestMtu in BluetoothGatt to change the MTU to 185, and then onMtuChanged return mtu value is 185 and status Is GATT_SUCCESS. When we send the longer message which is about 40 bytes, the return value of writeCharacteristic of BluetoothGatt is true, but the onCharacteristicWrite callback will give us a 133 status code, which is not GATT_SUCCESS. Then the devices will disconnect.

Why do I need a larger MTU: What we expect is to be able to use a bigger MTU so that we can send data to end-device to provision Wi-Fi credentials. The end device was built to only accept the credentials all at one time(assumes MTU>23 wouldn’t be an issue. So we have field devices that customers can’t use.

Question: Is there any ideas or workaround that we can try?

Phone info: Samsung s10e Os: Android 10 Android security patch: March 1, 2020

End-Device info: ESP32

tyler
  • 1,273
  • 2
  • 16
  • 40
  • Have you tried using for example the nRF Connect app to see that the behaviour is the same there? Have you checked the Android HCI log or used a BLE sniffer to see which messages are really sent and received? – Emil Apr 08 '20 at 11:33
  • Yes, to the nRF Connect app. I’m going down the logs and sniffer route now, but fearing the worst. – tyler Apr 08 '20 at 11:49
  • New learning today: Any MTU setting between 24 & 512 results in only allowing for a payload of 20 characters. You can set the MTU to 24 and send 20 characters all day. you can set the MTU to 512 and send 20 characters all day…but if you try 21 characters in either example the phone will “NOT” send the payload, but it think it does send it…then the timeout happens and the connection is closed. – tyler Apr 08 '20 at 21:52
  • This seems very wrong or buggy somewhere. Were you able to check the packet log? – Emil Apr 08 '20 at 22:29
  • @Emil I'll have the logs later this week. I've never checked out the "btsnoop log" before, but my hunch is it's just going to say...(sent packet...no response...timeout). Do the "btsnoop log" really go deeper than that? What tools do you recommend to capture the packet log? – tyler Apr 09 '20 at 01:23
  • @tyler currently I'm facing same issue. If I explicitly set the MTU value apart from request with max MTU value, we get the callback at first connection. But when re-connected again, the default 20mtu is received... which is very annoying.... – Waqar UlHaq Sep 15 '20 at 20:53
  • There's a long thread on the Samsung dev forums on this matter: https://forum.developer.samsung.com/t/samsung-android-10-ble-connectivity-regression/509 – Alessandro Mulloni Feb 10 '21 at 15:21

2 Answers2

1

This is not an issue with your code, but rather an issue with Samsung's BLE stack. Essentially Samsung improperly does nothing when calling the setMTU method. The way to allow writes to work correctly is to wait for the 133 status code, wait some time for the device to disconnect, then call the BLE connect method and try the write again. Calling other BLE methods may cause the failure again. The logic should look as follows:

Write to characteristic. If status is 133 in the onCharacteristicWrite, wait for the device to disconnect. Call the connect method. When connected, retry the write (perhaps with some exponential backoff of the process).

1

I'm not using a Samsung device, but I had the same issue with a Sunmi device and apparently the problem is attempting to send the full MTU or any value close to that. If I negotiate 512 and attempt to send 512 I get a 133 status followed by a disconnection, but if I negotiate 512 and send 500 bytes the whole thing runs fine. Right now I'm using this formula, but I have only tested it for an MTU negotiated value of 512:

private int getMtu() {
    return Math.max(GattClient.DEFAULT_MTU, (int)(this.mtu * .99));
}

The idea is that you'll always send at least 20 bytes, since that works fine even if the MTU is not negotiated. Other than that, I send only 99% of the MTU. I'm not getting the 133 status anymore.

André Fratelli
  • 5,920
  • 7
  • 46
  • 87