メモの日々


2024年11月26日(火) [長年日記]

[android][net] AndroidのBLEを使った通信で512バイトを超える送信データが破棄される

AndroidのBluetooth APIを使ったBLE通信で、送信データが破棄されれ送信されないことがあったので調べた。

原因はAndroid 13と14での動作の変更だった。

Android 13での変更

ちゃんとした資料は見つけられなかったが、Android 13から512バイトを超えるデータを送受信できなくなったというissueがちらほら見つかる。これなど。

次の変更が原因のようだ。

この変更は、次に示すBluetoothの仕様でattribute valueの最大長が512バイトに制限されていることへ準じるために行われている。

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では次の変更がされたとある。

これは、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の人による説明があった。

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.