Open ghost opened 6 years ago
Why doesn't this exist? It should be quite simple to specify and implement, right?
Assuming that this information is consistently available across all platforms where Web Bluetooth is supported then this should be implementable.
For prioritization, it's important for us to understand what applications this feature is blocking. @bluetooth-mdw already mentioned Bluetooth Mesh. @Emill, what application are you trying to port to Web Bluetooth?
There are a bunch of applications where the specifications assume the MTU is known. For my case I'd like to implement Flic 2: https://github.com/50ButtonsEach/flic2-documentation/wiki/Flic-2-Protocol-Specification, where the fragmentation/segmentation can be much more efficient if the MTU is known (especially if LE Data Length extension is supported). Otherwise one have to be on the safe side and assume 23.
On Mac, Windows and BlueZ, the MTU is hardcoded and always negotiated by the system when a connection starts before GATT is activated for the user. On Android it's customizable but there's as far as I know no reason not to try setting it to 517 (maximum supported).
However, now when you mention it, the remote device may select a lower MTU than requested, and I'm not sure if that value can be retrieved from all platforms. :/ Mac: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1620312-maximumwritevaluelength Android: onMtuChanged BlueZ: ? Windows: Maybe https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.genericattributeprofile.gattsession.maxpdusize#Windows_Devices_Bluetooth_GenericAttributeProfile_GattSession_MaxPduSize can be used?
Knowing the MTU is one aspect of this. Being able to negotiate an increase in MTU size is what the ExchangeMTU ATT PDU is really for though and an increased MTU can have a massive positive impact on data rates (i.e. throughput or latency, depending on perspective). For example, transferring 100K of data using write commands but with the default MTU of 23, could take around 8 seconds whereas transferring the same data in the same way with an MTU of 247 would probably take less than a second.
I think it's sane that the system automatically negotiates the highest MTU the Bluetooth stack can handle at the beginning of the connection.
Android is currently broken both in its API and its implementation and doesn't follow the Bluetooth spec. It says the following about Exchange MTU:
This subprocedure shall only be initiated once during a connection.
However, every app can call requestMtu
with its own MTU value and the stack will happily send out a new ATT_EXCHANGE_MTU_REQ
with the app-supplied value every time requestMtu
is called.
Hi, Has there been any movement on this in the last 2 years? It seems like an important feature for web bluetooth on Android. I'm dealing with this issue on a project now.
No. Recent activity has been elsewhere, e.g. getDevices watchAdvertisements https://twitter.com/Vincent_Scheib/status/1296585373204660224 and writeValueWithResponse https://blog.chromium.org/2020/07/chrome-85-upload-streaming-human.html#:~:text=Web%20Bluetooth%20writeValueWithResponse (and without), Microsoft Edge shipping Web Bluetooth, etc. writeValueWithoutResponse might help you if the problem is bandwidth.
On Wed, Aug 12, 2020 at 2:13 PM AndrejHronco notifications@github.com wrote:
Hi, Has there been any movement on this in the last 2 years? It seems like an important feature for web bluetooth on Android. I'm dealing with this issue on a project now.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/WebBluetoothCG/web-bluetooth/issues/383#issuecomment-673114034, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAC6PG3KAYDR7V5DZL65NT3SAMAXBANCNFSM4ENL3ZEQ .
This feature could be really useful for a lot of Applications.
There are a bunch of applications where the specifications assume the MTU is known. For my case I'd like to implement Flic 2: https://github.com/50ButtonsEach/flic2-documentation/wiki/Flic-2-Protocol-Specification, where the fragmentation/segmentation can be much more efficient if the MTU is known (especially if LE Data Length extension is supported). Otherwise one have to be on the safe side and assume 23.
On Mac, Windows and BlueZ, the MTU is hardcoded and always negotiated by the system when a connection starts before GATT is activated for the user. On Android it's customizable but there's as far as I know no reason not to try setting it to 517 (maximum supported).
However, now when you mention it, the remote device may select a lower MTU than requested, and I'm not sure if that value can be retrieved from all platforms. :/ Mac: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1620312-maximumwritevaluelength Android: onMtuChanged BlueZ: ? Windows: Maybe https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.genericattributeprofile.gattsession.maxpdusize#Windows_Devices_Bluetooth_GenericAttributeProfile_GattSession_MaxPduSize can be used?
@Emill You're right
We've implement QuickBlue
plugin of Flutter on Android/iOS/macOS/Windows
QuickBlue
is extracted from NotepadCore
: https://github.com/woodemi/notepad_core
requestMtu
was implemented on Web in NotepadCore
as well: https://github.com/woodemi/notepad_core/blob/cbf649b44576b808a5fd5d9ce0a2e592492e84a6/notepad_core_web/lib/notepad_core_web.dart#L105
Though there isn't MTU API in WebBluetooth, both Chrome & Edge(Chromiume) works with MTU <= 104 on macOS
, or with expextedMtu
<= 517 on Windows 10+ (Our device work with 247 after negotiation)
Flutter on macOS negotiate with our device by 247
Have on idea why Chromiume works only with 104
Maybe related: https://github.com/WebBluetoothCG/web-bluetooth/issues/284#issuecomment-244738626
Could we have this API in the year 2021?
Acknowledging this. Google staffing & priorities don't have this being implemented now. Clear product / partner descriptions help identify the need when this is compared to other work items. Chromium is also open source, other contributors can submit patches.
A survey on the availability of upstream APIs:
func maximumWriteValueLength(for type: CBCharacteristicWriteType) -> Int
So, this API is not easy.
* Win32/UWP: No.
* BlueZ: Yes (IMO).
I think this is a no. The "mtu" property is server-only, which means it is not available to clients (which is what WebBluetooth is). Or is there some other way to get it?
@dlech I may have misunderstood what you mean by "server-only", but the maximum value of MTU supported by each of the client and server is exchanged and then "Both devices then use the minimum of these exchanged values for all further communication" (from the Bluetooth core specification) so, it's definitely a property that both client and server use.
Thanks for @dlech 's correction. I thought that I had searched win32 API thoroughly.
GattSession.MaxPduSize
it's definitely a property that both client and server use.
Yes, but I don't think there is a D-Bus API to read this number.
@dlech OK, I understand what you meant now. Thanks.
Yes, but I don't think there is a D-Bus API to read this number.
BlueZ issue requesting the feature: https://github.com/bluez/bluez/issues/199
An interesting question brought up in the BlueZ issue is "should the MTU value be per-device or per-attribute"? The feature most people are requesting is "I want the MTU that is negotiated when the device connects", but the argument was made that with Bluetooth 5.2 Enhanced Attribute Protocol (EATT), it might make more sense to have a per-attribute property (and that this could even be different for tx vs rx).
Hi everyone! Any news? With a ridiculous MTU fixed to 20 usable bytes on Android, there is just no way to use bluetooth of some apps.
I'm working with IOT devices and PWAs. Since the mixed content policy changed and self signed certificates stopped to be recognized by Chrome, it is also impossible to communicate over Wifi (http or websocket).
To resume, there is no way to communicate with a IOT device from a PWA because of the choices of Google :s
Hi everyone! Any news? With a ridiculous MTU fixed to 20 usable bytes on Android, there is just no way to use bluetooth of some apps.
You need requestConnectionPriority
on Android: https://github.com/woodemi/notepad_core/blob/b0e329f3d6e02f14f8a0e5e48a6ddb48e026b658/notepad_core/android/src/main/kotlin/io/woodemi/notepad_core/NotepadCorePlugin.kt#L124-L136
"requestMtu" -> {
connectGatt?.requestMtu(call.argument<Int>("expectedMtu")!!)
result.success(null)
}
"requestConnectionPriority" -> {
val bleConnectionPriority = call.argument<String>("bleConnectionPriority")!!
connectGatt?.requestConnectionPriority(when (bleConnectionPriority) {
"high" -> BluetoothGatt.CONNECTION_PRIORITY_HIGH
"lowPower" -> BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER
else -> BluetoothGatt.CONNECTION_PRIORITY_BALANCED
})
result.success(null)
}
The trickiest part is to make sure we can provide this functionality to all platforms where Web Bluetooth is supported: Android, Chrome OS, (Linux,) macOS, and Windows.
Hi everyone! Any news? With a ridiculous MTU fixed to 20 usable bytes on Android, there is just no way to use bluetooth of some apps.
I'm working with IOT devices and PWAs. Since the mixed content policy changed and self signed certificates stopped to be recognized by Chrome, it is also impossible to communicate over Wifi (http or websocket).
To resume, there is no way to communicate with a IOT device from a PWA because of the choices of Google :s
You can let the device tell JS app the negotiated MTU through the value of a characteristic.
Thank you for your replies.
I'm trying to solve it using the web api only for a PWA (no Android native code used in an hybrid app).
The Android platform allows to make a MTU request: If I open my web app and connect a bluetooth device, I will receive data limited to 20 bytes. Meanwhile, if I open a native app to make the MTU request (I use nRFConnect) and I back to the web app, I receive the entire data. The Bluetooth Web API is great but a method to call this 'simple' request is missing.
So yes, a workarround may be to listen these short notifications and read a characteritic to get the entire data (the read method can get the value through multiple packet, the MTU is not a problem) but it is no efficient.
I'm sorry for my bad english :)
One thing that may be worth doing is adding a hint when connecting the GATT server to request a higher MTU when possible.
const device = await navigator.bluetooth.requestDevice({ filters: [{ services: [0x1234] }] });
// Try requesting for a larger ATT MTU so that more information can be exchanged per transmission.
const server = await device.gatt.connect({ largeMtuHint: true });
...
It would be up to the implementation to honor this hint as much as possible based on the platform.
On Android, it would call mBluetoothGatt.requestMtu(512)
for instance. On macOS, it would be a no-op. Etc.
What do you think @reillyeon?
Another idea would be to simply update our Android implementation and always call mBluetoothGatt.requestMtu(512)
when connecting to the GATT server if that doesn't cause any harm.
@reillyeon Shall I do this behind a flag so that we can try it out in the wild?
517 is a more common MTU since then we can always send the maximum sized characteristic value length (512 bytes) regardless of packet type. But note my comment at https://github.com/WebBluetoothCG/web-bluetooth/issues/383#issuecomment-584047487.
Another idea would be to simply update our Android implementation and always call
mBluetoothGatt.requestMtu(512)
when connecting to the GATT server if that doesn't cause any harm.@reillyeon Shall I do this behind a flag so that we can try it out in the wild?
That is what some operating systems are doing. I noticed Windows make by itself a request to increase the MTU (255 bytes).
@Emill Thanks. requestMtu(517)
will be then! What about your comment though? Shall we not do this or is there a way to monitor MTU changes to maintain the one we've set?
517 sounds great 😍
Last time I checked, Android will automatically send the onMtuChanged callback immediately after onConnectionStateChange (when indicating connected), if there is already an established MTU. This can for example happen when another app on the phone is already connected and the MTU exchange has already taken place. The documentation for onMtuChanged indeed hints this can happen.
In this case, to be able to distinguish this automatic callback from a manual one triggered as a response to requestMtu, and to not accidentally executing more than one request in parallel (which Android doesn't like), a flow that works is to do the following:
I haven't tested if onMtuChanged will be triggered as well if another app calls requestMtu while connected. In this case and the mtu value changes from a previously negotiated one, Android's behaviour is broken since the BLE spec only allows one mtu exchange for the duration of a connection.
Thanks for all these details @Emill. I've just updated my Chromium build with a naive approach that works locally. See https://chromium-review.googlesource.com/c/chromium/src/+/3260011
I'll have to update it with your findings though.
FYI This change is now in Chrome Canary. Please give it a try and let me know if that works for you.
See https://twitter.com/quicksave2k/status/1460550201064763402 as well
Hi everyone
I made some basic tests and it works on my Android device with the Canary!
This issue has veered off topic. The Android MTU negotiation issue should probably be discussed at https://crbug.com/1164621. The original issue here is a request for a browser API to retrieve the negotiated MTU on all platforms.
Yes, but I don't think there is a D-Bus API to read this number.
BlueZ issue requesting the feature: bluez/bluez#199
An update on this... a D-Bus API for this was added recently.
Unlike other platforms where the API provides a per-device value, BlueZ provides this on a per-characteristic basis. The rational being that Bluetooth 5.2 Enhanced Attribute Protocol (EATT) could result in characteristics having different individual MTU values.
So I propose that the Web Bluetooth API should also be a property/method on the characteristic object. The implementation on Linux/ChromOS can use the new BlueZ API while the Windows and Mac implementations can just return the device-level value for now and could be modified in the future if Microsoft or Apple adds new APIs for EATT.
Also, I think it makes sense to perhaps have an API like Apple's maximumWriteValueLength()
rather than returning the MTU. If the problem we are solving with this is "what is the maximum number bytes can I pass to writeWithoutResponse()
without getting an error", then it makes sense to return that number directly rather than expecting consumers of the API to know that you have to subtract 3 from the MTU to arrive at the correct answer.
If we read the Bluetooth standard, we also see that MTU is also important when receiving notifications, since the MTU then tells how long notifications might be. If a client notices it retrieves a maximum length notification, it might want to issue a read operation to read the rest of the value. So the MTU is not only useful when writing. To continue on your route, in that case it might be an option to add a flag to the notification event that this is a maximum sized notification. Same could possibly be useful for read, in case the remote device does not support Long reads, then we know the value has been truncated to at most x bytes.
Hasn't the problem been solved yet?
Hello from 2023 , and no the problem has not been addressed.
I still need it :s
We will use https://bugs.chromium.org/p/chromium/issues/detail?id=1435103 to track this from an implementation perspective.
We would also need a simple getNegotiatedMTU()
or similar.
hello from 2024. Still unfixed it seems :((((
That is so bad 😭
@morgan-wild Is there any workaround for this? I desperately need it. How can i transfer 20 bytes over and over again to transfer lots of continuos data. I can't figure it out. Would really appreciate some help.
Nothing for the moment with the stable version or Chrome. I tried Canary one time during this discussion because a modified version with a bigger MTU was published and it worked.
Yes, as shared earlier in https://github.com/WebBluetoothCG/web-bluetooth/issues/383#issuecomment-970120381, we've changed Web Bluetooth behavior on Android (only) so that a larger ATT MTU is requested. But there's no API to request/change MTU yet.
Hi @beaufortfrancois
It is effective on the regular version of Chrome Android?
Bluetooth mesh runs over Bluetooth LE and defines a proxy node and associated proxy protocol to allow standard (non-mesh) Bluetooth LE clients to talk to a mesh network.
The proxy protocol requires you to know the MTU which was negotiated behind the scenes by client and server so that packets can be filled and segmentation used when messages exceed the size of the MTU. MTU negotiation happens silently behind the scenes in Web Bluetooth and there's no API that lets you find out what the outcome of that negotiation was. This is needed for the reasons given. Please consider adding an API such as getNeogitatedMTU().
Thanks
-- [edit: Related chromium Issue 1164621: WebBluetooth: not implemented "Exchange MTU" step]