I found the problem... In my code I had defined the onDescriptorWrite() callback incorrectly. After fixing this I managed to receive callbacks when writing the descriptor succeeded.
With that solved, the solution I found is to make BTLE operations blocking by creating a class in charge of sequencing the operations.
The class I created is pretty simple, a BlockingQueue on a separate thread to enqueue incoming operations.
BTLE operations are enqueued by calling writeDescriptor(), writeCharacteristic() methods,a separate thread starts executing the operations when free, and the gattCallback informs the thread when operations have been completed, in order to proceed with the next operation.
Queue class:
public BtleAsynchronousOperationThread(SensorAbstract sensor, BluetoothGatt gatt) {
log("Creating Btle Operation thread");
this.gatt = gatt;
this.sensor = sensor;
}
public void enable() {
log("Enabling btle command thread");
queueReadySemaphore.release();
}
public void writeDescriptor(BluetoothGattDescriptor descriptor) throws BTCommunicationException {
BtleAsynchronousCommand command = new BtleAsynchronousCommand(descriptor);
try {
queue.put(command);
} catch (InterruptedException e) {
throw new BTCommunicationException("Error while writing the descriptor");
}
}
public void writeCharacteristic(BluetoothGattCharacteristic characteristic) throws BTCommunicationException {
BtleAsynchronousCommand command = new BtleAsynchronousCommand(
BtleAsynchronousCommand.CommandType.WRITE_CHARACTERISTIC, characteristic);
try {
queue.put(command);
} catch (InterruptedException e) {
throw new BTCommunicationException("Error while writing the characteristic");
}
}
public void readCharacteristic(BluetoothGattCharacteristic characteristic) throws BTCommunicationException {
BtleAsynchronousCommand command = new BtleAsynchronousCommand(
BtleAsynchronousCommand.CommandType.READ_CHARACTERISTIC, characteristic);
try {
queue.put(command);
} catch (InterruptedException e) {
throw new BTCommunicationException("Error while reading the characteristic");
}
}
public void cancel() {
this.interrupt();
}
public void writeCompleted() {
queueReadySemaphore.release();
}
@Override
public void run() {
BtleAsynchronousCommand command;
try {
while (!(Thread.currentThread().isInterrupted())) {
queueReadySemaphore.acquire();
log("Waiting for BTLE command");
command = queue.take();
switch (command.getCommandType()) {
case WRITE_DESCRIPTOR:
log("Starting to write descriptor:" + command.getDescriptor().getUuid());
if (!gatt.writeDescriptor(command.getDescriptor())) {
throw new BTCommunicationException("Error while writing the descriptor");
}
break;
case WRITE_CHARACTERISTIC:
log("Starting to write characteristic:" + command.getCharacteristic().getUuid());
if (!gatt.writeCharacteristic(command.getCharacteristic())) {
throw new BTCommunicationException("Error while writing the characteristic");
}
break;
case READ_CHARACTERISTIC:
log("Starting to read characteristic:" + command.getCharacteristic().getUuid());
if (!gatt.readCharacteristic(command.getCharacteristic())) {
throw new BTCommunicationException("Error while writing the characteristic");
}
break;
default:
log("Unknown command received");
break;
}
}
} catch (InterruptedException e) {
log("Btle thread interrupted, closing.");
} catch (BTCommunicationException e) {
log("Error while reading:" + e.getMessage());
sensor.retry();
}
gatt = null;
queue = null;
queueReadySemaphore = null;
Thread.currentThread().interrupt();
}
}
When defining the BluetoothCallback:
mGattCallback = new BluetoothGattCallback() {
//Other methods can be defined around here, such as
//onConnectionStateChange(), etc
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
onSensorCharacteristicChangedInner(gatt, characteristic);
}
@Override
public void onDescriptorWrite (BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status){
log("onDescriptorWrite:"+descriptor.getUuid()+ " status:"+status);
btleCommunicationThread.writeCompleted();
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,BluetoothGattCharacteristic characteristic, int status) {
log("onCharacteristicWrite:"+characteristic.getUuid()+ " status:"+status);
btleCommunicationThread.writeCompleted();
}