Skip to content

Commit d4b1ef4

Browse files
authored
Merge pull request #580 from NordicSemiconductor/bugfix/notify-cancelled-tasks
Bug fix: Cancel tasks when they are removed from the task queue
2 parents 790c37c + 74dc3cb commit d4b1ef4

File tree

2 files changed

+65
-20
lines changed

2 files changed

+65
-20
lines changed

ble/src/main/java/no/nordicsemi/android/ble/BleManagerHandler.java

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,7 @@ public void onReceive(final Context context, final Intent intent) {
261261
&& previousState != BluetoothAdapter.STATE_OFF) {
262262
// No more calls are possible
263263
operationInProgress = true;
264-
taskQueue.clear();
265-
initQueue = null;
264+
emptyTasks(FailCallback.REASON_BLUETOOTH_DISABLED);
266265
ready = false;
267266

268267
final BluetoothDevice device = bluetoothDevice;
@@ -546,8 +545,7 @@ void close() {
546545
// Setting this flag to false would allow to enqueue a new request before the
547546
// current one ends processing. The following line should not be uncommented.
548547
// mGattCallback.operationInProgress = false;
549-
taskQueue.clear();
550-
initQueue = null;
548+
emptyTasks(FailCallback.REASON_DEVICE_DISCONNECTED);
551549
initialization = false;
552550
bluetoothDevice = null;
553551
connected = false;
@@ -561,6 +559,37 @@ void close() {
561559
}
562560
}
563561

562+
/**
563+
* This method clears the task queues and notifies removed requests of cancellation.
564+
* @param status the reason of cancellation.
565+
*/
566+
private void emptyTasks(final int status) {
567+
final BluetoothDevice oldBluetoothDevice = bluetoothDevice;
568+
if (initQueue != null) {
569+
for (final Request task : initQueue) {
570+
if (oldBluetoothDevice != null)
571+
task.notifyFail(oldBluetoothDevice, status);
572+
else
573+
task.notifyInvalidRequest();
574+
}
575+
initQueue = null;
576+
}
577+
for (final Request task : taskQueue) {
578+
if (oldBluetoothDevice != null) {
579+
if (status == FailCallback.REASON_BLUETOOTH_DISABLED ||
580+
task.characteristic != null ||
581+
task.descriptor != null) {
582+
task.notifyFail(oldBluetoothDevice, status);
583+
} else {
584+
task.notifyFail(oldBluetoothDevice, FailCallback.REASON_CANCELLED);
585+
}
586+
} else {
587+
task.notifyInvalidRequest();
588+
}
589+
}
590+
taskQueue.clear();
591+
}
592+
564593
public BluetoothDevice getBluetoothDevice() {
565594
return bluetoothDevice;
566595
}
@@ -776,7 +805,7 @@ private boolean internalDisconnect(final int reason) {
776805
log(Log.DEBUG, () -> "gatt.disconnect()");
777806
gatt.disconnect();
778807
if (wasConnected)
779-
return true;
808+
return;
780809

781810
// If the device wasn't connected, there will be no callback after calling
782811
// gatt.disconnect(), the connection attempt will be stopped.
@@ -1618,8 +1647,7 @@ final void enqueue(@NonNull final Request request) {
16181647

16191648
@Override
16201649
final void cancelQueue() {
1621-
taskQueue.clear();
1622-
initQueue = null;
1650+
emptyTasks(FailCallback.REASON_CANCELLED);
16231651
initialization = false;
16241652

16251653
final BluetoothDevice device = bluetoothDevice;
@@ -1655,7 +1683,7 @@ final void cancelCurrent() {
16551683
// Cancelling a Reliable Write request requires sending Abort command.
16561684
// Instead of notifying failure, we will remove all enqueued tasks and
16571685
// let the nextRequest to sent Abort command.
1658-
requestQueue.cancelQueue();
1686+
requestQueue.notifyAndCancelQueue(device);
16591687
} else if (requestQueue != null) {
16601688
requestQueue.notifyFail(device, FailCallback.REASON_CANCELLED);
16611689
requestQueue = null;
@@ -2251,8 +2279,7 @@ public void onConnectionStateChange(@NonNull final BluetoothGatt gatt,
22512279
}
22522280

22532281
operationInProgress = true; // no more calls are possible
2254-
taskQueue.clear();
2255-
initQueue = null;
2282+
emptyTasks(FailCallback.REASON_DEVICE_DISCONNECTED);
22562283
ready = false;
22572284

22582285
// Store the current value of the connected and deviceNotSupported flags...
@@ -2447,8 +2474,7 @@ public void onServiceChanged(@NonNull final BluetoothGatt gatt) {
24472474
manager.onServicesInvalidated();
24482475
onDeviceDisconnected();
24492476
// Clear queues, services are no longer valid.
2450-
taskQueue.clear();
2451-
initQueue = null;
2477+
emptyTasks(FailCallback.REASON_NULL_ATTRIBUTE);
24522478
// And discover services again
24532479
serviceDiscoveryRequested = true;
24542480
servicesDiscovered = false;
@@ -2525,7 +2551,7 @@ public void onCharacteristicWrite(final BluetoothGatt gatt,
25252551
final boolean valid = wr.notifyPacketSent(gatt.getDevice(), characteristic.getValue());
25262552
if (!valid && requestQueue instanceof ReliableWriteRequest) {
25272553
wr.notifyFail(gatt.getDevice(), FailCallback.REASON_VALIDATION);
2528-
requestQueue.cancelQueue();
2554+
requestQueue.notifyAndCancelQueue(gatt.getDevice());
25292555
} else if (wr.hasMore()) {
25302556
enqueueFirst(wr);
25312557
} else {
@@ -2549,7 +2575,7 @@ public void onCharacteristicWrite(final BluetoothGatt gatt,
25492575
request.notifyFail(gatt.getDevice(), status);
25502576
// Automatically abort Reliable Write when write error happen
25512577
if (requestQueue instanceof ReliableWriteRequest)
2552-
requestQueue.cancelQueue();
2578+
requestQueue.notifyAndCancelQueue(gatt.getDevice());
25532579
}
25542580
awaitingRequest = null;
25552581
onError(gatt.getDevice(), ERROR_WRITE_CHARACTERISTIC, status);
@@ -2649,7 +2675,7 @@ public void onDescriptorWrite(final BluetoothGatt gatt,
26492675
final boolean valid = wr.notifyPacketSent(gatt.getDevice(), data);
26502676
if (!valid && requestQueue instanceof ReliableWriteRequest) {
26512677
wr.notifyFail(gatt.getDevice(), FailCallback.REASON_VALIDATION);
2652-
requestQueue.cancelQueue();
2678+
requestQueue.notifyAndCancelQueue(gatt.getDevice());
26532679
} else if (wr.hasMore()) {
26542680
enqueueFirst(wr);
26552681
} else {
@@ -2673,7 +2699,7 @@ public void onDescriptorWrite(final BluetoothGatt gatt,
26732699
request.notifyFail(gatt.getDevice(), status);
26742700
// Automatically abort Reliable Write when write error happen
26752701
if (requestQueue instanceof ReliableWriteRequest)
2676-
requestQueue.cancelQueue();
2702+
requestQueue.notifyAndCancelQueue(gatt.getDevice());
26772703
}
26782704
awaitingRequest = null;
26792705
onError(gatt.getDevice(), ERROR_WRITE_DESCRIPTOR, status);
@@ -2707,8 +2733,7 @@ public void onCharacteristicChanged(
27072733
manager.onServicesInvalidated();
27082734
onDeviceDisconnected();
27092735
// Clear queues, services are no longer valid.
2710-
taskQueue.clear();
2711-
initQueue = null;
2736+
emptyTasks(FailCallback.REASON_NULL_ATTRIBUTE);
27122737
serviceDiscoveryRequested = true;
27132738
log(Log.VERBOSE, () -> "Discovering Services...");
27142739
log(Log.DEBUG, () -> "gatt.discoverServices()");
@@ -3826,8 +3851,7 @@ private synchronized void nextRequest(final boolean force) {
38263851
awaitingRequest.notifyFail(bluetoothDevice, FailCallback.REASON_NULL_ATTRIBUTE);
38273852
awaitingRequest = null;
38283853
}
3829-
taskQueue.clear();
3830-
initQueue = null;
3854+
emptyTasks(FailCallback.REASON_NULL_ATTRIBUTE);
38313855
final BluetoothGatt bluetoothGatt = this.bluetoothGatt;
38323856
if (connected && bluetoothGatt != null) {
38333857
// Invalidate all services and characteristics

ble/src/main/java/no/nordicsemi/android/ble/RequestQueue.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
package no.nordicsemi.android.ble;
2424

25+
import android.bluetooth.BluetoothDevice;
2526
import android.os.Handler;
2627

2728
import androidx.annotation.IntRange;
@@ -171,6 +172,9 @@ public boolean isEmpty() {
171172
* Cancels all the enqueued operations that were not executed yet.
172173
* The one currently executed will be cancelled, if it is {@link TimeoutableRequest}.
173174
* <p>
175+
* Cancelled operations will NOT be notified with {@link FailCallback#REASON_CANCELLED},
176+
* they will be just removed from the queue.
177+
* <p>
174178
* It is safe to call this method in {@link Request#done(SuccessCallback)} or
175179
* {@link Request#fail(FailCallback)} callback;
176180
*/
@@ -210,4 +214,21 @@ boolean hasMore() {
210214
void cancelQueue() {
211215
requests.clear();
212216
}
217+
218+
/**
219+
* Clears the queue, but first notifies all remaining tasks about cancellation.
220+
* @param device the connected device.
221+
*/
222+
void notifyAndCancelQueue(final @NonNull BluetoothDevice device) {
223+
for (final Request request : requests) {
224+
request.notifyFail(device, FailCallback.REASON_CANCELLED);
225+
}
226+
cancelQueue();
227+
}
228+
229+
@Override
230+
void notifyFail(@NonNull BluetoothDevice device, int status) {
231+
super.notifyFail(device, status);
232+
notifyAndCancelQueue(device);
233+
}
213234
}

0 commit comments

Comments
 (0)