2024年11月26日(火) [長年日記]
■ [android][net] AndroidのBLEを使った通信で512バイトを超える送信データが破棄される
AndroidのBluetooth APIを使ったBLE通信で、送信データが破棄されれ送信されないことがあったので調べた。
原因はAndroid 13と14での動作の変更だった。
Android 13での変更
ちゃんとした資料は見つけられなかったが、Android 13から512バイトを超えるデータを送受信できなくなったというissueがちらほら見つかる。これなど。
次の変更が原因のようだ。
- Set maximum attribute value to defined by spec Core 5.3 (android.googlesource.com)
この変更は、次に示すBluetoothの仕様でattribute valueの最大長が512バイトに制限されていることへ準じるために行われている。
- 3.2.9. Long attribute values (Bluetooth Core Specification 6.0)
The maximum length of an attribute value shall be 512 octets.
どうも、Bluetoothでデータ送信を行う際のデータ長の上限としてATT(Attribute Protocol)のMTUサイズから3バイト引いた値を使うコードが多いようで(手元のコードもそうなっていた)、AndroidのATT MTUサイズは最大で517バイトになり得て、そうなったときに 517 - 3 = 515 バイトのデータを送信しようとしてしまい、その結果Android 12までであれば送信できていたのにAndroid 13になったら送信できなくなったという問題があるようだ。
Android 14での変更
Android 14では次の変更がされたとある。
- MTU is set to 517 for the first GATT client requesting an MTU (Android Developers)
これは、BluetoothGatt#requestMtu()によりMTUサイズの拡張を要求したときに引数の値によらず常に517バイトが使われるということのようだ。
手元のコードでは、上述したAndroid 13での変更に対する対策として BluetoothGatt#requestMtu() で515を使うようになっていたが、この対策はAndroid 14以降では意味がなくなってしまうことになる。
変更への対応方法
送信データの長さの上限を (MTUサイズ - 3) とするだけでは問題があるということになる。
送信データの長さの上限は (min(512, MTUサイズ - 3)) とする必要がある。
AndroidのATT MTUサイズの上限が517バイトなのは何故か
検索すると、AndroidのATT MTUサイズの上限は517バイトではなく515バイトであるべきだと書いている人を見かける。これに対しては、次のissueにGoogleの人による説明があった。
- [BLE] - Incoming packets longer then 515 dropped after BluetoothGatt.requestMtu(517) used, even though remote peer supports MTU 517 (Android Public Tracker)
I would like to provide you more details about why 517 was chosen instead of 515:
The number 517 was selected because the GATT_MAX_MTU_SIZE constant was set to 517 since the initial drop of Android Bluetooths stack in 2012.
A deeper reason is that the Bluetooth Specification allows the maximum size of an ATT attribute to be 512 bytes and the largest command ATT_PREPARE_WRITE_REQ has 5 bytes of header. Hence 512 + 5 = 517.