Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 803d71a2 authored by Robert Wu's avatar Robert Wu
Browse files

Bluetooth MIDI: Set MTU after service discovery

ag/15888883 introduced a change to set MTUs. This change would set MTUs
once the device is connected.

We got reports that packets get dropped after ~20 bytes on some BLE
MIDI connections.

It seems like setting MTU only works after a successful service
discovery, rather simply after calling service discovery. That is,
requestMtu() must wait for the onServicesDiscovered() callback instead
of simply after calling discoverServices().

BluetoothMidiService assumed that requestMtu() would either return false
or have a callback. However, for some BLE MIDI devices, neither would
occur. This results in BluetoothMidiService using a buffer size of 512
bytes when many devices on the market only supports 23 bytes. This
results in only the first ~20 bytes getting sent for a packet.

The fix here is to call requestMtu() after onServicesDiscovered() as
well as to set default encoder and decoder sizes in case requestMtu()
fails.

Bug: 246515282
Test: Connected Yamaha UD-BT01 with Android. Sent 100 bytes of SysEx to
verify data gets sent correctly. Verifed that data gets logged correctly
on MidiScope on the other side.

Change-Id: I8f7e9bc98064b110010a9af7aea56c786ef9e374
parent 2303aff0
Loading
Loading
Loading
Loading
+18 −10
Original line number Diff line number Diff line
@@ -100,16 +100,12 @@ public final class BluetoothMidiDevice {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status,
                int newState) {
            Log.d(TAG, "onConnectionStateChange() status: " + status + ", newState: " + newState);
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                Log.d(TAG, "Connected to GATT server.");
                Log.d(TAG, "Attempting to start service discovery:" +
                        mBluetoothGatt.discoverServices());
                if (!mBluetoothGatt.requestMtu(MAX_PACKET_SIZE)) {
                    Log.e(TAG, "request mtu failed");
                    mPacketEncoder.setMaxPacketSize(DEFAULT_PACKET_SIZE);
                    mPacketDecoder.setMaxPacketSize(DEFAULT_PACKET_SIZE);
                }
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                Log.i(TAG, "Disconnected from GATT server.");
                close();
@@ -118,6 +114,7 @@ public final class BluetoothMidiDevice {

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            Log.d(TAG, "onServicesDiscovered() status: " +  status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                BluetoothGattService service = gatt.getService(MIDI_SERVICE);
                if (service != null) {
@@ -137,6 +134,13 @@ public final class BluetoothMidiDevice {
                        // Specification says to read the characteristic first and then
                        // switch to receiving notifications
                        mBluetoothGatt.readCharacteristic(characteristic);

                        // Request higher MTU size
                        if (!gatt.requestMtu(MAX_PACKET_SIZE)) {
                            Log.e(TAG, "request mtu failed");
                            mPacketEncoder.setMaxPacketSize(DEFAULT_PACKET_SIZE);
                            mPacketDecoder.setMaxPacketSize(DEFAULT_PACKET_SIZE);
                        }
                    }
                }
            } else {
@@ -233,13 +237,13 @@ public final class BluetoothMidiDevice {
            System.arraycopy(buffer, 0, mCachedBuffer, 0, count);

            if (DEBUG) {
                logByteArray("Sent ", mCharacteristic.getValue(), 0,
                       mCharacteristic.getValue().length);
                logByteArray("Sent ", mCachedBuffer, 0, mCachedBuffer.length);
            }

            if (mBluetoothGatt.writeCharacteristic(mCharacteristic, mCachedBuffer,
                    mCharacteristic.getWriteType()) != BluetoothGatt.GATT_SUCCESS) {
                Log.w(TAG, "could not write characteristic to Bluetooth GATT");
            int result = mBluetoothGatt.writeCharacteristic(mCharacteristic, mCachedBuffer,
                    mCharacteristic.getWriteType());
            if (result != BluetoothGatt.GATT_SUCCESS) {
                Log.w(TAG, "could not write characteristic to Bluetooth GATT. result: " + result);
                return false;
            }

@@ -252,6 +256,10 @@ public final class BluetoothMidiDevice {
        mBluetoothDevice = device;
        mService = service;

        // Set a small default packet size in case there is an issue with configuring MTUs.
        mPacketEncoder.setMaxPacketSize(DEFAULT_PACKET_SIZE);
        mPacketDecoder.setMaxPacketSize(DEFAULT_PACKET_SIZE);

        mBluetoothGatt = mBluetoothDevice.connectGatt(context, false, mGattCallback);

        mContext = context;