mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
ble: MTU handling in TransactionBuilder including MTUs > 515 (#5146)
This is especially relevant for Android 14 https://developer.android.com/about/versions/14/behavior-changes-all#mtu-set-to-517 AbstractBTLEDeviceSupport.calcMaxWriteChunk: new function to properly calculate chunk size TransactionBuilder.writeChunkedData: if required automatically resize the chunk size TransactionBuilder.write and TransactionBuilder.writeLegacy: for now log an error but still attempt to write the data
This commit is contained in:
committed by
José Rebelo
parent
1445f35799
commit
8794e11720
+15
-1
@@ -22,7 +22,7 @@ import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
@@ -84,4 +84,18 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport
|
||||
|
||||
@Nullable
|
||||
abstract BluetoothGattCharacteristic getCharacteristic(UUID uuid, int deviceIdx);
|
||||
|
||||
abstract int getMTU(int deviceIdx);
|
||||
|
||||
/// the maximum payload length supported for one write action
|
||||
@IntRange(from = 20L, to = 512L)
|
||||
public static int calcMaxWriteChunk(int mtu) {
|
||||
// the minimum MTU is 23 (Bluetooth spec)
|
||||
int safeMtu = Math.max(23, mtu);
|
||||
|
||||
// GATT_MAX_ATTR_LEN: no larger than 512 (Bluetooth spec)
|
||||
// MTU: overhead of simple write must be supported. Some other operations like
|
||||
// ATT_PREPARE_WRITE_REQ have even larger overhead so the max BLE MTU is larger than 512+3
|
||||
return Math.min(512, safeMtu - 3);
|
||||
}
|
||||
}
|
||||
+1
@@ -584,6 +584,7 @@ public abstract class AbstractBTLEMultiDeviceSupport extends AbstractBTLEDeviceS
|
||||
/**
|
||||
* Get the current MTU, or the minimum 23 if unknown
|
||||
*/
|
||||
@Override
|
||||
public int getMTU(int deviceIdx) {
|
||||
validateDeviceIndex(deviceIdx);
|
||||
return mMTUs[deviceIdx];
|
||||
|
||||
+8
@@ -524,4 +524,12 @@ public abstract class AbstractBTLESingleDeviceSupport extends AbstractBTLEDevice
|
||||
public int getMTU() {
|
||||
return mMTU;
|
||||
}
|
||||
|
||||
@Override
|
||||
int getMTU(int deviceIdx) {
|
||||
if(deviceIdx != 0){
|
||||
throw new IllegalArgumentException("deviceIdx is " + deviceIdx);
|
||||
}
|
||||
return getMTU();
|
||||
}
|
||||
}
|
||||
|
||||
+33
-5
@@ -112,6 +112,14 @@ public class TransactionBuilder {
|
||||
LOG.warn("Unable to write characteristic: null");
|
||||
return this;
|
||||
}
|
||||
|
||||
int maxChunk = getMaxWriteChunk();
|
||||
if (data.length > maxChunk) {
|
||||
LOG.error("writeLegacy - payload for {} is too long: {} > {}",
|
||||
characteristic.getUuid(), data.length, maxChunk);
|
||||
// TODO throw exception after reviewing device specific code (performConnected...)
|
||||
}
|
||||
|
||||
WriteAction action = new WriteAction(characteristic, data, true);
|
||||
return add(action);
|
||||
}
|
||||
@@ -119,15 +127,23 @@ public class TransactionBuilder {
|
||||
/// Invokes a write operation on a given characteristic
|
||||
/// The result status will be made available asynchronously through
|
||||
/// {@link GattCallback#onCharacteristicWrite(BluetoothGatt, BluetoothGattCharacteristic, int)}
|
||||
/// @see #write(UUID, byte...)
|
||||
/// @see #write(UUID, byte...)
|
||||
/// @see #writeChunkedData(BluetoothGattCharacteristic, byte[], int)
|
||||
/// @see #writeLegacy(BluetoothGattCharacteristic, byte...)
|
||||
/// @see #writeLegacy(BluetoothGattCharacteristic, byte...)
|
||||
@NonNull
|
||||
public TransactionBuilder write(@Nullable BluetoothGattCharacteristic characteristic, byte... data) {
|
||||
if (characteristic == null) {
|
||||
LOG.warn("Unable to write characteristic: null");
|
||||
return this;
|
||||
}
|
||||
|
||||
int maxChunk = getMaxWriteChunk();
|
||||
if (data.length > maxChunk) {
|
||||
LOG.error("write - payload for {} is too long: {} > {}",
|
||||
characteristic.getUuid(), data.length, maxChunk);
|
||||
// TODO throw exception after reviewing device specific code (performConnected...)
|
||||
}
|
||||
|
||||
WriteAction action = new WriteAction(characteristic, data);
|
||||
return add(action);
|
||||
}
|
||||
@@ -135,7 +151,7 @@ public class TransactionBuilder {
|
||||
/// Invokes a write operation on a given characteristic
|
||||
/// The result status will be made available asynchronously through
|
||||
/// {@link GattCallback#onCharacteristicWrite(BluetoothGatt, BluetoothGattCharacteristic, int)}
|
||||
/// @see #write(BluetoothGattCharacteristic, byte...)
|
||||
/// @see #write(BluetoothGattCharacteristic, byte...)
|
||||
/// @see #writeChunkedData(BluetoothGattCharacteristic, byte[], int)
|
||||
/// @see #writeLegacy(BluetoothGattCharacteristic, byte...)
|
||||
@NonNull
|
||||
@@ -151,17 +167,22 @@ public class TransactionBuilder {
|
||||
/// Invokes one or more write operations on a given characteristic
|
||||
/// The result status will be made available asynchronously through
|
||||
/// {@link GattCallback#onCharacteristicWrite(BluetoothGatt, BluetoothGattCharacteristic, int)}
|
||||
/// @param requestedChunkLength will be automatically reduced if required for this connection
|
||||
/// @see #write(BluetoothGattCharacteristic, byte...)
|
||||
/// @see #write(UUID, byte...)
|
||||
/// @see #writeLegacy(BluetoothGattCharacteristic, byte...)
|
||||
@NonNull
|
||||
public TransactionBuilder writeChunkedData(@Nullable BluetoothGattCharacteristic characteristic,
|
||||
@NonNull byte[] data, int chunkSize)
|
||||
{
|
||||
@NonNull byte[] data,
|
||||
@IntRange(from = 1L) int requestedChunkLength) {
|
||||
if (characteristic == null) {
|
||||
LOG.warn("Unable to write characteristic: null");
|
||||
return this;
|
||||
}
|
||||
|
||||
// no larger than requested
|
||||
int chunkSize = Math.min(requestedChunkLength, getMaxWriteChunk());
|
||||
|
||||
for (int start = 0; start < data.length; start += chunkSize) {
|
||||
int end = start + chunkSize;
|
||||
if (end > data.length) end = data.length;
|
||||
@@ -172,6 +193,13 @@ public class TransactionBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/// the maximum payload length supported for one write action
|
||||
@IntRange(from = 20L, to = 512L)
|
||||
public int getMaxWriteChunk() {
|
||||
int mtu = mDeviceSupport.getMTU(mDeviceIdx);
|
||||
return AbstractBTLEDeviceSupport.calcMaxWriteChunk(mtu);
|
||||
}
|
||||
|
||||
/// Calls {@link BluetoothGatt#requestMtu(int)}. Results are returned asynchronously through
|
||||
/// {@link GattCallback#onMtuChanged(BluetoothGatt, int, int)}
|
||||
@NonNull
|
||||
|
||||
Reference in New Issue
Block a user