Bluetooth Command Queuing for Android

Bluetooth 4 (aka BLE – Bluetooth Low Energy) on Android has some important limitations that need to be addressed in apps that need a high degree of user interactivity with a Bluetooth device. Very quickly when you set out to write such an app you’ll come to realize that you’ll need some sort of Bluetooth command queuing system.

First, a little background. Recently I had to develop a client app that would act as a control panel to a prototype hardware device using BLE. This app had to allow commands to be sent to the device on-demand from the user through a UI with a lot of “moving parts”. I’ve worked with synchronizing fitness wearables, but this was a whole different ball game! Fitness wearables tend to be a simple sync and download mechanism, but for this new device, user’s could tap around a set of controls, with each interaction issuing a command (or even a set of commands) to a device. Add to this the complexity that the app would also need to receive ad-hoc notifications at any time from the device, and would also have to send periodic timed commands in the background!

Since Android 4.3 the android.bluetooth classes support BLE, but I came to realize that they would not be enough this time to deal with the complexity alone. It’s because Android’s underlying BLE implementation is a bit quirky. The infamous google issue 58381 illustrates. Other helpful nuggets of information can be gleaned at this stack overflow post here. The critical piece of information from user OneWorld in that posting is: “Gatt always can process one command at a time. If several commands get called short after another, the first one gets cancelled due to the synchronous nature of the gatt implementation.”

I’ve come up against this problem myself. You can issue multiple BLE commands ad-hoc from a multi-threaded app, but BLE won’t respond very well. If you issue a second command before it has a chance to deal with the previous one, the BLE stack can (particularly on Android 4.3) get into a state where it won’t even respond anymore and even require a manual user restart of Bluetooth! Not ideal. You can delay before sending a second command, but to cover all possibilities in hardware and BLE conditions, this introduces an unnecessary delay into every command. Fortunately, in my app, I could assume (like most BLE applications) that the device responds to a command. So I could send the next command immediately after receiving the command response back (via a BLE notification). To create a robust, stable, experience for the user, I had to implement a queuing system layer on top of Android’s bluetooth classes so that the Android BLE stack was assured of only having to deal with one command (or event) at a time.

bluetoothqueue_screenshot
Let’s look at the details of implementing the command queue.
(The code for a sample app showing the concept is in my github.)

The first thing we need is an object to represent a command. This can be extended in subclasses to do any kind of fancy thing you want, e.g. perform multiple writes to different characteristics.
For this example, we’ll just have a simple command that reads the device’s serial number. To see the idea of the queue in action the DelayCommand will also pause briefly before doing the read, so that you can see commands backing-up in the queue.

public class DelayCommand extends BluetoothCommand {
    public void execute(BluetoothGatt gatt){
        try {
            synchronized (Thread.currentThread()) {
                Thread.currentThread().wait(500);
            }
        }catch(InterruptedException e){
            //ignore
        }

        //As an example, read from serial number characteristic
        gatt.readCharacteristic(
            gatt.getService(com.movisens.smartgattlib.Service.DEVICE_INFORMATION)
                .getCharacteristic(Characteristic.SERIAL_NUMBER_STRING));
    }
}

The main activity can create a command object and add it to the queue by calling the service’s method queueCommand, and that’s where we really see the queue in-action:

synchronized (mCommandQueue) {
            mCommandQueue.add(command);  //Add to end of stack
            //...
            ExecuteCommandRunnable runnable = new ExecuteCommandRunnable(command);
            mCommandExecutor.execute(runnable);
        }

The first thing to note is the mCommandQueue is simply a Java LinkedList, which works as a FIFO queue.

When the command is added to the queue a runnable is also created that actually executes the command. These runnables are executed on a Single Thread Executor to ensure only one is run at a time. Here’s the Runnable code that actually does the work of executing the command:

    class ExecuteCommandRunnable implements Runnable{

        BluetoothCommand mCommand;

        public ExecuteCommandRunnable(BluetoothCommand command) {
            mCommand = command;
        }

        @Override
        public void run() {
            //Acquire semaphore lock to ensure no other operations can run until this one completed
            mCommandLock.acquireUninterruptibly();
            //Tell the command to start itself.
            mCommand.execute(mBluetoothGatt);
        }
    };

You might think it’s enough to just run a queue of runnables off a single thread to implement the queue, right? Wrong. Here’s why.
When mCommand.execute is called, it starts the bluetooth read and returns immmediately. This would cause the next runnable
to run its command. Remember what I said about Android BLE not liking when you issue a new command if the first hasn’t responded yet?
So here’s the trick: we also need a lock that will prevent the next runnable from executing it’s command until the previous command
has a response. This is done in the BLE GattCallback, where the characteristic read response actually comes back to the caller:


    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        super.onCharacteristicRead(gatt, characteristic, status);
        if(characteristic.getUuid().equals(Characteristic.SERIAL_NUMBER_STRING)){
            //... Send string response to listener here..
            dequeueCommand();
        }
    }

    protected void dequeueCommand(){
        mCommandQueue.pop();
        //...
        mCommandLock.release();
    }

DequeueCommand releases the lock, and the next runnable then waiting to acquire the lock can run it’s own command.
The lock is just a semaphore with a limit of 1 permit:

Semaphore mCommandLock = new Semaphore(1,true);

And there you have it – the beginnings of a BLE command queue.

A more complete implementation has to allow for other important cases, such as the device failing to respond to the command at all ( – quick answer: implement a time-out to release the command lock and continue to the next command – ), but that’s for another post!