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

Commit 90d53f23 authored by Robert Wu's avatar Robert Wu
Browse files

Accumulate Bluetooth MIDI packets until ready

Bluetooth MIDI uses a packet encoder to encode and send packets.
When a caller calls send too quickly, the Bluetooth characteristic
is not ready and BluetoothPacketEncoder waits for a packet that
never arrives.

With this change, if the sender sends a couple packets before the
connection is ready, they will be added to a queue. Once the
Bluetooth characteristic is avaliable, this queue will be sent
with the next packet. This way, we can remove this infinite loop.

Bug: 64598842
Test: Added code to Phil's MidiBtlePairing app to send data to a
bluetooth MIDI device repeatedly after pairing. Verified that packets
don't get sent correctly before the change and that packets do get
sent correctly with this change.

Change-Id: Ib261d871752e83a2150980cd50acb56ee318b034
parent 3688b228
Loading
Loading
Loading
Loading
+14 −4
Original line number Diff line number Diff line
@@ -211,10 +211,10 @@ public final class BluetoothMidiDevice {
        }

        @Override
        public void writePacket(byte[] buffer, int count) {
        public boolean writePacket(byte[] buffer, int count) {
            if (mCharacteristic == null) {
                Log.w(TAG, "not ready to send packet yet");
                return;
                return false;
            }

            // Cache the previous buffer for writePacket so buffers aren't
@@ -223,12 +223,22 @@ public final class BluetoothMidiDevice {
                mCachedBuffer = new byte[count];
            }
            System.arraycopy(buffer, 0, mCachedBuffer, 0, count);
            mCharacteristic.setValue(mCachedBuffer);
            if (!mCharacteristic.setValue(mCachedBuffer)) {
                Log.w(TAG, "could not set characteristic value");
                return false;
            }

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

            if (!mBluetoothGatt.writeCharacteristic(mCharacteristic)) {
                Log.w(TAG, "could not write characteristic to Bluetooth GATT");
                return false;
            }

            return true;
        }
    }

+42 −2
Original line number Diff line number Diff line
@@ -17,11 +17,14 @@
package com.android.bluetoothmidiservice;

import android.media.midi.MidiReceiver;
import android.util.Log;

import com.android.internal.midi.MidiConstants;
import com.android.internal.midi.MidiFramer;

import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Queue;

/**
 * This class accumulates MIDI messages to form a MIDI packet.
@@ -52,6 +55,8 @@ public class BluetoothPacketEncoder extends PacketEncoder {

    private final Object mLock = new Object();

    private Queue<byte[]> mFailedToSendQueue = new ArrayDeque<byte[]>();

    // This receives normalized data from mMidiFramer and accumulates it into a packet buffer
    private final MidiReceiver mFramedDataReceiver = new MidiReceiver() {
        @Override
@@ -59,6 +64,8 @@ public class BluetoothPacketEncoder extends PacketEncoder {
                throws IOException {

            synchronized (mLock) {
                flushFailedToSendQueueLocked();

                int milliTimestamp = (int)(timestamp / MILLISECOND_NANOS) & MILLISECOND_MASK;
                byte status = msg[offset];
                boolean isSysExStart = (status == MidiConstants.STATUS_SYSTEM_EXCLUSIVE);
@@ -227,11 +234,44 @@ public class BluetoothPacketEncoder extends PacketEncoder {
        }

        if (mAccumulatedBytes > 0) {
            mPacketReceiver.writePacket(mAccumulationBuffer, mAccumulatedBytes);
            boolean wasSendSuccessful = mPacketReceiver.writePacket(mAccumulationBuffer,
                    mAccumulatedBytes);

            if (!wasSendSuccessful) {
                byte[] failedBuffer = new byte[mAccumulatedBytes];
                System.arraycopy(mAccumulationBuffer, 0, failedBuffer, 0, mAccumulatedBytes);
                mFailedToSendQueue.add(failedBuffer);
                Log.d(TAG, "Enqueued data into failed queue.");
            }

            mAccumulatedBytes = 0;
            mPacketTimestamp = 0;
            mRunningStatus = 0;
            mWritePending = true;
            mWritePending = wasSendSuccessful;
        }
    }

    private void flushFailedToSendQueueLocked() {
        while (!mFailedToSendQueue.isEmpty()) {
            while (mWritePending) {
                try {
                    mLock.wait();
                } catch (InterruptedException e) {
                    // try again
                    continue;
                }
            }
            byte[] currentBuffer = mFailedToSendQueue.element();

            boolean wasSendSuccessful = mPacketReceiver.writePacket(currentBuffer,
                    currentBuffer.length);
            mWritePending = wasSendSuccessful;
            if (wasSendSuccessful) {
                mFailedToSendQueue.remove();
                Log.d(TAG, "Dequeued data from failed queue.");
            } else {
                return;
            }
        }
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -30,8 +30,9 @@ public abstract class PacketEncoder extends MidiReceiver {
        /** Called to write an accumulated packet.
         * @param buffer the packet buffer to write
         * @param count the number of bytes in the packet buffer to write
         * @return whether the operation was successful
         */
        public void writePacket(byte[] buffer, int count);
        boolean writePacket(byte[] buffer, int count);
    }

    /**
+2 −1
Original line number Diff line number Diff line
@@ -114,7 +114,7 @@ public class BluetoothMidiCodecTest {
        // TODO Should this block?
        // Store the packets and then write them from a periodic task.
        @Override
        public void writePacket(byte[] buffer, int count) {
        public boolean writePacket(byte[] buffer, int count) {
            Log.d(TAG, "writePacket() passed " + MidiFramer.formatMidiData(buffer, 0, count));
            byte[] packet = new byte[count];
            System.arraycopy(buffer, 0, packet, 0, count);
@@ -124,6 +124,7 @@ public class BluetoothMidiCodecTest {
                assertEquals(null, e);
            }
            Log.d(TAG, "writePacket() returns");
            return true;
        }

        void test(final byte[][] midi)
+2 −1
Original line number Diff line number Diff line
@@ -42,11 +42,12 @@ public class BluetoothMidiEncoderTest {
    static class AccumulatingPacketReceiver implements PacketEncoder.PacketReceiver {
        ArrayList<byte[]> mBuffers = new ArrayList<byte[]>();

        public void writePacket(byte[] buffer, int count) {
        public boolean writePacket(byte[] buffer, int count) {
            byte[] actualRow = new byte[count];
            Log.d(TAG, "writePacket() passed " + MidiFramer.formatMidiData(buffer, 0, count));
            System.arraycopy(buffer, 0, actualRow, 0, count);
            mBuffers.add(actualRow);
            return true;
        }

        byte[][] getBuffers() {