summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuha Vuolle <juha.vuolle@insta.fi>2021-11-29 15:47:01 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2021-12-17 13:24:07 +0000
commitdd60d061f83721a86d388659d97cc493ee09f90a (patch)
tree1e5c3b7c36c6becb106143ceb28927a87f24417c
parent7738e8762a211cc72d38633d3b0346d50db47113 (diff)
Android BT LE Client thread protection improvement
The Android BT LE Client is a threaded feature; the Java-side callbacks from Android execute in different threads and JNI calls from Qt/C++ execute in the Qt thread. This commit augments the existing protection by synchronizing the accesses to shared variables on the client object. Task-number: QTBUG-98351 Change-Id: Icb39499c42bbdeec5e65ed5368294e1c631d3bfa Reviewed-by: Andreas Buhr <andreas.buhr@qt.io> (cherry picked from commit a8e6de3863c89db251e6420a70351860c375b3f3) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/android/bluetooth/src/org/qtproject/qt/android/bluetooth/QtBluetoothLE.java1043
1 files changed, 541 insertions, 502 deletions
diff --git a/src/android/bluetooth/src/org/qtproject/qt/android/bluetooth/QtBluetoothLE.java b/src/android/bluetooth/src/org/qtproject/qt/android/bluetooth/QtBluetoothLE.java
index 6ce0b0d2..b4a41307 100644
--- a/src/android/bluetooth/src/org/qtproject/qt/android/bluetooth/QtBluetoothLE.java
+++ b/src/android/bluetooth/src/org/qtproject/qt/android/bluetooth/QtBluetoothLE.java
@@ -128,6 +128,12 @@ public class QtBluetoothLE {
private int pendingJobHandle = -1;
};
+ // The handleOn* functions in this class are callback handlers which are synchronized
+ // to "this" client object. This protects the member variables which could be
+ // concurrently accessed from Qt (JNI) thread and different Java threads *)
+ // *) The newer Android API (starting Android 8.1) synchronizes callbacks to one
+ // Java thread, but this is not true for the earlier API which we still support.
+ //
// In case bond state has been changed due to access to a restricted handle,
// Android never completes the operation which triggered the devices to bind
// and thus never fires on(Characteristic|Descriptor)(Read|Write) callback,
@@ -136,50 +142,54 @@ public class QtBluetoothLE {
// re-add the currently pending job to the queue's head and re-run it.
// If, by some reason, bonding process has been interrupted, either
// re-add the currently pending job to the queue's head and re-run it.
- private class BondStateBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mBluetoothGatt == null)
- return;
+ private synchronized void handleOnReceive(Context context, Intent intent)
+ {
+ if (mBluetoothGatt == null)
+ return;
- final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- if (device == null || !device.getAddress().equals(mBluetoothGatt.getDevice().getAddress()))
- return;
+ final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ if (device == null || !device.getAddress().equals(mBluetoothGatt.getDevice().getAddress()))
+ return;
- final int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
- final int previousBondState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1);
+ final int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
+ final int previousBondState =
+ intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1);
- if (bondState == BluetoothDevice.BOND_BONDING) {
- synchronized (readWriteQueue) {
- if (pendingJob == null || pendingJob.jobType == IoJobType.Mtu)
- return;
- }
+ if (bondState == BluetoothDevice.BOND_BONDING) {
+ if (pendingJob == null || pendingJob.jobType == IoJobType.Mtu)
+ return;
- timeoutHandler.removeCallbacksAndMessages(null);
- handleForTimeout.set(HANDLE_FOR_RESET);
- } else if (previousBondState == BluetoothDevice.BOND_BONDING && (bondState == BluetoothDevice.BOND_BONDED || bondState == BluetoothDevice.BOND_NONE)) {
- synchronized (readWriteQueue) {
- if (pendingJob == null || pendingJob.jobType == IoJobType.Mtu)
- return;
+ timeoutHandler.removeCallbacksAndMessages(null);
+ handleForTimeout.set(HANDLE_FOR_RESET);
+ } else if (previousBondState == BluetoothDevice.BOND_BONDING &&
+ (bondState == BluetoothDevice.BOND_BONDED || bondState == BluetoothDevice.BOND_NONE)) {
+ if (pendingJob == null || pendingJob.jobType == IoJobType.Mtu)
+ return;
- readWriteQueue.addFirst(pendingJob);
- pendingJob = null;
- }
+ readWriteQueue.addFirst(pendingJob);
+ pendingJob = null;
- performNextIO();
- } else if (previousBondState == BluetoothDevice.BOND_BONDED && bondState == BluetoothDevice.BOND_NONE) {
- // peripheral or central removed the bond information;
- // if it was peripheral, the connection attempt would fail with PIN_OR_KEY_MISSING,
- // which is handled by Android by broadcasting ACTION_BOND_STATE_CHANGED
- // with new state BOND_NONE, without actually deleting the bond information :facepalm:
- // if we get there, it is safer to delete it now, by invoking the undocumented API call
- try {
- device.getClass().getMethod("removeBond").invoke(device);
- } catch (Exception ex) {
- ex.printStackTrace();
- }
+ performNextIO();
+ } else if (previousBondState == BluetoothDevice.BOND_BONDED
+ && bondState == BluetoothDevice.BOND_NONE) {
+ // peripheral or central removed the bond information;
+ // if it was peripheral, the connection attempt would fail with PIN_OR_KEY_MISSING,
+ // which is handled by Android by broadcasting ACTION_BOND_STATE_CHANGED
+ // with new state BOND_NONE, without actually deleting the bond information :facepalm:
+ // if we get there, it is safer to delete it now, by invoking the undocumented API call
+ try {
+ device.getClass().getMethod("removeBond").invoke(device);
+ } catch (Exception ex) {
+ ex.printStackTrace();
}
}
+ }
+
+ private class BondStateBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ handleOnReceive(context, intent);
+ }
};
private BroadcastReceiver bondStateBroadcastReceiver = null;
@@ -203,11 +213,11 @@ public class QtBluetoothLE {
/*************************************************************/
/* Device scan */
+ /* Returns true, if request was successfully completed */
+ /* This function is called from Qt thread, but only accesses */
+ /* variables that are not accessed from Java threads */
/*************************************************************/
- /*
- Returns true, if request was successfully completed
- */
public boolean scanForLeDevice(final boolean isEnabled) {
if (isEnabled == mLeScanRunning)
return true;
@@ -261,343 +271,422 @@ public class QtBluetoothLE {
public native void leScanResult(long qtObject, BluetoothDevice device, int rssi, byte[] scanRecord);
- /*************************************************************/
- /* Service Discovery */
- /*************************************************************/
-
- private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
-
- public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
- if (qtObject == 0)
- return;
+ private synchronized void handleOnConnectionStateChange(BluetoothGatt gatt,
+ int status, int newState) {
+ if (qtObject == 0)
+ return;
- int qLowEnergyController_State = 0;
- //This must be in sync with QLowEnergyController::ControllerState
- switch (newState) {
- case BluetoothProfile.STATE_DISCONNECTED:
- if (bondStateBroadcastReceiver != null) {
- qtContext.unregisterReceiver(bondStateBroadcastReceiver);
- bondStateBroadcastReceiver = null;
- }
+ int qLowEnergyController_State = 0;
+ //This must be in sync with QLowEnergyController::ControllerState
+ switch (newState) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ if (bondStateBroadcastReceiver != null) {
+ qtContext.unregisterReceiver(bondStateBroadcastReceiver);
+ bondStateBroadcastReceiver = null;
+ }
- qLowEnergyController_State = 0;
- // we disconnected -> get rid of data from previous run
- resetData();
- // reset mBluetoothGatt, reusing same object is not very reliable
- // sometimes it reconnects and sometimes it does not.
- if (mBluetoothGatt != null) {
- mBluetoothGatt.close();
- if (mHandler != null) {
- mHandler.getLooper().quitSafely();
- mHandler = null;
- }
- }
- mBluetoothGatt = null;
- break;
- case BluetoothProfile.STATE_CONNECTED:
- if (bondStateBroadcastReceiver == null) {
- bondStateBroadcastReceiver = new BondStateBroadcastReceiver();
- qtContext.registerReceiver(bondStateBroadcastReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
+ qLowEnergyController_State = 0;
+ // we disconnected -> get rid of data from previous run
+ resetData();
+ // reset mBluetoothGatt, reusing same object is not very reliable
+ // sometimes it reconnects and sometimes it does not.
+ if (mBluetoothGatt != null) {
+ mBluetoothGatt.close();
+ if (mHandler != null) {
+ mHandler.getLooper().quitSafely();
+ mHandler = null;
}
- qLowEnergyController_State = 2;
- }
-
- //This must be in sync with QLowEnergyController::Error
- int errorCode;
- switch (status) {
- case BluetoothGatt.GATT_SUCCESS:
- errorCode = 0; break; //QLowEnergyController::NoError
- case BluetoothGatt.GATT_FAILURE: // Android's equivalent of "do not know what error it is"
- errorCode = 1; break; //QLowEnergyController::UnknownError
- case 8: // BLE_HCI_CONNECTION_TIMEOUT
- Log.w(TAG, "Connection Error: Try to delay connect() call after previous activity");
- errorCode = 5; break; //QLowEnergyController::ConnectionError
- case 19: // BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION
- case 20: // BLE_HCI_REMOTE_DEV_TERMINATION_DUE_TO_LOW_RESOURCES
- case 21: // BLE_HCI_REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF
- Log.w(TAG, "The remote host closed the connection");
- errorCode = 7; //QLowEnergyController::RemoteHostClosedError
- break;
- case 22: // BLE_HCI_LOCAL_HOST_TERMINATED_CONNECTION
- // Internally, Android maps PIN_OR_KEY_MISSING to GATT_CONN_TERMINATE_LOCAL_HOST
- errorCode = 8; break; //QLowEnergyController::AuthorizationError
- default:
- Log.w(TAG, "Unhandled error code on connectionStateChanged: " + status + " " + newState);
- errorCode = status; break; //TODO deal with all errors
- }
- leConnectionStateChange(qtObject, errorCode, qLowEnergyController_State);
+ }
+ mBluetoothGatt = null;
+ break;
+ case BluetoothProfile.STATE_CONNECTED:
+ if (bondStateBroadcastReceiver == null) {
+ bondStateBroadcastReceiver = new BondStateBroadcastReceiver();
+ qtContext.registerReceiver(bondStateBroadcastReceiver,
+ new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
+ }
+ qLowEnergyController_State = 2;
}
- public void onServicesDiscovered(BluetoothGatt gatt, int status) {
- //This must be in sync with QLowEnergyController::Error
- int errorCode;
- StringBuilder builder = new StringBuilder();
- switch (status) {
- case BluetoothGatt.GATT_SUCCESS:
- errorCode = 0; //QLowEnergyController::NoError
- final List<BluetoothGattService> services = mBluetoothGatt.getServices();
- for (BluetoothGattService service: services) {
- builder.append(service.getUuid().toString()).append(" "); //space is separator
- }
- break;
- default:
- Log.w(TAG, "Unhandled error code on onServicesDiscovered: " + status);
- errorCode = status; break; //TODO deal with all errors
- }
- leServicesDiscovered(qtObject, errorCode, builder.toString());
+ //This must be in sync with QLowEnergyController::Error
+ int errorCode;
+ switch (status) {
+ case BluetoothGatt.GATT_SUCCESS:
+ errorCode = 0; //QLowEnergyController::NoError
+ break;
+ case BluetoothGatt.GATT_FAILURE: // Android's equivalent of "do not know what error"
+ errorCode = 1; //QLowEnergyController::UnknownError
+ break;
+ case 8: // BLE_HCI_CONNECTION_TIMEOUT
+ Log.w(TAG, "Connection Error: Try to delay connect() call after previous activity");
+ errorCode = 5; //QLowEnergyController::ConnectionError
+ break;
+ case 19: // BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION
+ case 20: // BLE_HCI_REMOTE_DEV_TERMINATION_DUE_TO_LOW_RESOURCES
+ case 21: // BLE_HCI_REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF
+ Log.w(TAG, "The remote host closed the connection");
+ errorCode = 7; //QLowEnergyController::RemoteHostClosedError
+ break;
+ case 22: // BLE_HCI_LOCAL_HOST_TERMINATED_CONNECTION
+ // Internally, Android maps PIN_OR_KEY_MISSING to GATT_CONN_TERMINATE_LOCAL_HOST
+ errorCode = 8; //QLowEnergyController::AuthorizationError
+ break;
+ default:
+ Log.w(TAG, "Unhandled error code on connectionStateChanged: "
+ + status + " " + newState);
+ errorCode = status;
+ break; //TODO deal with all errors
+ }
+ leConnectionStateChange(qtObject, errorCode, qLowEnergyController_State);
+ }
- scheduleMtuExchange();
+ private synchronized void handleOnServicesDiscovered(BluetoothGatt gatt, int status) {
+ //This must be in sync with QLowEnergyController::Error
+ int errorCode;
+ StringBuilder builder = new StringBuilder();
+ switch (status) {
+ case BluetoothGatt.GATT_SUCCESS:
+ errorCode = 0; //QLowEnergyController::NoError
+ final List<BluetoothGattService> services = mBluetoothGatt.getServices();
+ for (BluetoothGattService service: services) {
+ builder.append(service.getUuid().toString()).append(" "); //space is separator
+ }
+ break;
+ default:
+ Log.w(TAG, "Unhandled error code on onServicesDiscovered: " + status);
+ errorCode = status; break; //TODO deal with all errors
}
+ leServicesDiscovered(qtObject, errorCode, builder.toString());
+ if (status == BluetoothGatt.GATT_SUCCESS)
+ scheduleMtuExchange();
+ }
- public void onCharacteristicRead(android.bluetooth.BluetoothGatt gatt,
- android.bluetooth.BluetoothGattCharacteristic characteristic,
- int status)
- {
- int foundHandle = -1;
- synchronized (this) {
- foundHandle = handleForCharacteristic(characteristic);
- if (foundHandle == -1 || foundHandle >= entries.size() ) {
- Log.w(TAG, "Cannot find characteristic read request for read notification - handle: " +
- foundHandle + " size: " + entries.size());
-
- //unlock the queue for next item
- synchronized (readWriteQueue) {
- pendingJob = null;
- }
+ private synchronized void handleOnCharacteristicRead(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic,
+ int status)
+ {
+ int foundHandle = handleForCharacteristic(characteristic);
+ if (foundHandle == -1 || foundHandle >= entries.size() ) {
+ Log.w(TAG, "Cannot find characteristic read request for read notification - handle: " +
+ foundHandle + " size: " + entries.size());
- performNextIO();
- return;
- }
- }
+ //unlock the queue for next item
+ pendingJob = null;
- boolean requestTimedOut = !handleForTimeout.compareAndSet(
- modifiedReadWriteHandle(foundHandle, IoJobType.Read), HANDLE_FOR_RESET);
- if (requestTimedOut) {
- Log.w(TAG, "Late char read reply after timeout was hit for handle " + foundHandle);
- // Timeout has hit before this response -> ignore the response
- // no need to unlock pendingJob -> the timeout has done that already
- return;
- }
+ performNextIO();
+ return;
+ }
- GattEntry entry = entries.get(foundHandle);
- final boolean isServiceDiscoveryRun = !entry.valueKnown;
- entry.valueKnown = true;
+ boolean requestTimedOut = !handleForTimeout.compareAndSet(
+ modifiedReadWriteHandle(foundHandle, IoJobType.Read),
+ HANDLE_FOR_RESET);
+ if (requestTimedOut) {
+ Log.w(TAG, "Late char read reply after timeout was hit for handle " + foundHandle);
+ // Timeout has hit before this response -> ignore the response
+ // no need to unlock pendingJob -> the timeout has done that already
+ return;
+ }
+
+ GattEntry entry = entries.get(foundHandle);
+ final boolean isServiceDiscoveryRun = !entry.valueKnown;
+ entry.valueKnown = true;
+
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ // Qt manages handles starting at 1, in Java we use a system starting with 0
+ //TODO avoid sending service uuid -> service handle should be sufficient
+ leCharacteristicRead(qtObject,
+ characteristic.getService().getUuid().toString(),
+ foundHandle + 1, characteristic.getUuid().toString(),
+ characteristic.getProperties(), characteristic.getValue());
+ } else {
+ if (isServiceDiscoveryRun) {
+ Log.w(TAG, "onCharacteristicRead during discovery error: " + status);
- if (status == BluetoothGatt.GATT_SUCCESS) {
- // Qt manages handles starting at 1, in Java we use a system starting with 0
- //TODO avoid sending service uuid -> service handle should be sufficient
+ Log.d(TAG, "Non-readable characteristic " + characteristic.getUuid() +
+ " for service " + characteristic.getService().getUuid());
leCharacteristicRead(qtObject, characteristic.getService().getUuid().toString(),
- foundHandle + 1, characteristic.getUuid().toString(),
- characteristic.getProperties(), characteristic.getValue());
+ foundHandle + 1, characteristic.getUuid().toString(),
+ characteristic.getProperties(), characteristic.getValue());
} else {
- if (isServiceDiscoveryRun) {
- Log.w(TAG, "onCharacteristicRead during discovery error: " + status);
-
- Log.d(TAG, "Non-readable characteristic " + characteristic.getUuid() +
- " for service " + characteristic.getService().getUuid());
- leCharacteristicRead(qtObject, characteristic.getService().getUuid().toString(),
- foundHandle + 1, characteristic.getUuid().toString(),
- characteristic.getProperties(), characteristic.getValue());
- } else {
- // This must be in sync with QLowEnergyService::CharacteristicReadError
- final int characteristicReadError = 5;
- leServiceError(qtObject, foundHandle + 1, characteristicReadError);
- }
+ // This must be in sync with QLowEnergyService::CharacteristicReadError
+ final int characteristicReadError = 5;
+ leServiceError(qtObject, foundHandle + 1, characteristicReadError);
}
+ }
- if (isServiceDiscoveryRun) {
+ if (isServiceDiscoveryRun) {
- // last entry of pending service discovery run -> send discovery finished state update
- GattEntry serviceEntry = entries.get(entry.associatedServiceHandle);
- if (serviceEntry.endHandle == foundHandle)
- finishCurrentServiceDiscovery(entry.associatedServiceHandle);
- }
+ // last entry of pending service discovery run -> send discovery finished state update
+ GattEntry serviceEntry = entries.get(entry.associatedServiceHandle);
+ if (serviceEntry.endHandle == foundHandle)
+ finishCurrentServiceDiscovery(entry.associatedServiceHandle);
+ }
+
+ //unlock the queue for next item
+ pendingJob = null;
+
+ performNextIO();
+ }
+
+ private synchronized void handleOnCharacteristicChanged(android.bluetooth.BluetoothGatt gatt,
+ android.bluetooth.BluetoothGattCharacteristic characteristic)
+ {
+ int handle = handleForCharacteristic(characteristic);
+ if (handle == -1) {
+ Log.w(TAG,"onCharacteristicChanged: cannot find handle");
+ return;
+ }
+
+ leCharacteristicChanged(qtObject, handle+1, characteristic.getValue());
+ }
+
+ private synchronized void handleOnCharacteristicWrite(android.bluetooth.BluetoothGatt gatt,
+ android.bluetooth.BluetoothGattCharacteristic characteristic,
+ int status)
+ {
+ if (status != BluetoothGatt.GATT_SUCCESS)
+ Log.w(TAG, "onCharacteristicWrite: error " + status);
+
+ int handle = handleForCharacteristic(characteristic);
+ if (handle == -1) {
+ Log.w(TAG,"onCharacteristicWrite: cannot find handle");
+ return;
+ }
+
+ boolean requestTimedOut = !handleForTimeout.compareAndSet(
+ modifiedReadWriteHandle(handle, IoJobType.Write),
+ HANDLE_FOR_RESET);
+ if (requestTimedOut) {
+ Log.w(TAG, "Late char write reply after timeout was hit for handle " + handle);
+ // Timeout has hit before this response -> ignore the response
+ // no need to unlock pendingJob -> the timeout has done that already
+ return;
+ }
+
+ int errorCode;
+ //This must be in sync with QLowEnergyService::ServiceError
+ switch (status) {
+ case BluetoothGatt.GATT_SUCCESS:
+ errorCode = 0;
+ break; // NoError
+ default:
+ errorCode = 2;
+ break; // CharacteristicWriteError
+ }
+
+ byte[] value;
+ value = pendingJob.newValue;
+ pendingJob = null;
+
+ leCharacteristicWritten(qtObject, handle+1, value, errorCode);
+ performNextIO();
+ }
+
+ private synchronized void handleOnDescriptorRead(android.bluetooth.BluetoothGatt gatt,
+ android.bluetooth.BluetoothGattDescriptor descriptor,
+ int status)
+ {
+ int foundHandle = handleForDescriptor(descriptor);
+ if (foundHandle == -1 || foundHandle >= entries.size() ) {
+ Log.w(TAG, "Cannot find descriptor read request for read notification - handle: " +
+ foundHandle + " size: " + entries.size());
//unlock the queue for next item
- synchronized (readWriteQueue) {
- pendingJob = null;
- }
+ pendingJob = null;
performNextIO();
+ return;
}
- public void onCharacteristicWrite(android.bluetooth.BluetoothGatt gatt,
- android.bluetooth.BluetoothGattCharacteristic characteristic,
- int status)
- {
- if (status != BluetoothGatt.GATT_SUCCESS)
- Log.w(TAG, "onCharacteristicWrite: error " + status);
+ boolean requestTimedOut = !handleForTimeout.compareAndSet(
+ modifiedReadWriteHandle(foundHandle, IoJobType.Read),
+ HANDLE_FOR_RESET);
+ if (requestTimedOut) {
+ Log.w(TAG, "Late descriptor read reply after timeout was hit for handle " +
+ foundHandle);
+ // Timeout has hit before this response -> ignore the response
+ // no need to unlock pendingJob -> the timeout has done that already
+ return;
+ }
- int handle = handleForCharacteristic(characteristic);
- if (handle == -1) {
- Log.w(TAG,"onCharacteristicWrite: cannot find handle");
- return;
- }
+ GattEntry entry = entries.get(foundHandle);
+ final boolean isServiceDiscoveryRun = !entry.valueKnown;
+ entry.valueKnown = true;
- boolean requestTimedOut = !handleForTimeout.compareAndSet(
- modifiedReadWriteHandle(handle, IoJobType.Write), HANDLE_FOR_RESET);
- if (requestTimedOut) {
- Log.w(TAG, "Late char write reply after timeout was hit for handle " + handle);
- // Timeout has hit before this response -> ignore the response
- // no need to unlock pendingJob -> the timeout has done that already
- return;
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ //TODO avoid sending service and characteristic uuid -> handles should be sufficient
+ leDescriptorRead(qtObject,
+ descriptor.getCharacteristic().getService().getUuid().toString(),
+ descriptor.getCharacteristic().getUuid().toString(), foundHandle + 1,
+ descriptor.getUuid().toString(), descriptor.getValue());
+ } else {
+ if (isServiceDiscoveryRun) {
+ // Cannot read but still advertise the fact that we found a descriptor
+ // The value will be empty.
+ Log.w(TAG, "onDescriptorRead during discovery error: " + status);
+ Log.d(TAG, "Non-readable descriptor " + descriptor.getUuid() +
+ " for characteristic " + descriptor.getCharacteristic().getUuid() +
+ " for service " + descriptor.getCharacteristic().getService().getUuid());
+ leDescriptorRead(qtObject,
+ descriptor.getCharacteristic().getService().getUuid().toString(),
+ descriptor.getCharacteristic().getUuid().toString(), foundHandle + 1,
+ descriptor.getUuid().toString(), descriptor.getValue());
+ } else {
+ // This must be in sync with QLowEnergyService::DescriptorReadError
+ final int descriptorReadError = 6;
+ leServiceError(qtObject, foundHandle + 1, descriptorReadError);
}
- int errorCode;
- //This must be in sync with QLowEnergyService::ServiceError
- switch (status) {
- case BluetoothGatt.GATT_SUCCESS:
- errorCode = 0; break; // NoError
- default:
- errorCode = 2; break; // CharacteristicWriteError
+ }
+
+ if (isServiceDiscoveryRun) {
+ // last entry of pending service discovery run? ->send discovery finished state update
+ GattEntry serviceEntry = entries.get(entry.associatedServiceHandle);
+ if (serviceEntry.endHandle == foundHandle) {
+ finishCurrentServiceDiscovery(entry.associatedServiceHandle);
}
- byte[] value;
- synchronized (readWriteQueue) {
- value = pendingJob.newValue;
- pendingJob = null;
+ /* Some devices preset ClientCharacteristicConfiguration descriptors
+ * to enable notifications out of the box. However the additional
+ * BluetoothGatt.setCharacteristicNotification call prevents
+ * automatic notifications from coming through. Hence we manually set them
+ * up here.
+ */
+ if (descriptor.getUuid().compareTo(clientCharacteristicUuid) == 0) {
+ byte[] bytearray = descriptor.getValue();
+ final int value = (bytearray != null && bytearray.length > 0) ? bytearray[0] : 0;
+ // notification or indication bit set?
+ if ((value & 0x03) > 0) {
+ Log.d(TAG, "Found descriptor with automatic notifications.");
+ mBluetoothGatt.setCharacteristicNotification(
+ descriptor.getCharacteristic(), true);
+ }
}
- leCharacteristicWritten(qtObject, handle+1, value, errorCode);
- performNextIO();
}
- public void onCharacteristicChanged(android.bluetooth.BluetoothGatt gatt,
- android.bluetooth.BluetoothGattCharacteristic characteristic)
- {
- int handle = handleForCharacteristic(characteristic);
- if (handle == -1) {
- Log.w(TAG,"onCharacteristicChanged: cannot find handle");
- return;
- }
+ //unlock the queue for next item
+ pendingJob = null;
- leCharacteristicChanged(qtObject, handle+1, characteristic.getValue());
+ performNextIO();
+ }
+
+ private synchronized void handleOnDescriptorWrite(android.bluetooth.BluetoothGatt gatt,
+ android.bluetooth.BluetoothGattDescriptor descriptor,
+ int status)
+ {
+ if (status != BluetoothGatt.GATT_SUCCESS)
+ Log.w(TAG, "onDescriptorWrite: error " + status);
+
+ int handle = handleForDescriptor(descriptor);
+
+ boolean requestTimedOut = !handleForTimeout.compareAndSet(
+ modifiedReadWriteHandle(handle, IoJobType.Write),
+ HANDLE_FOR_RESET);
+ if (requestTimedOut) {
+ Log.w(TAG, "Late descriptor write reply after timeout was hit for handle " +
+ handle);
+ // Timeout has hit before this response -> ignore the response
+ // no need to unlock pendingJob -> the timeout has done that already
+ return;
}
- public void onDescriptorRead(android.bluetooth.BluetoothGatt gatt,
- android.bluetooth.BluetoothGattDescriptor descriptor,
- int status)
- {
- int foundHandle = -1;
- synchronized (this) {
- foundHandle = handleForDescriptor(descriptor);
- if (foundHandle == -1 || foundHandle >= entries.size() ) {
- Log.w(TAG, "Cannot find descriptor read request for read notification - handle: " +
- foundHandle + " size: " + entries.size());
-
- //unlock the queue for next item
- synchronized (readWriteQueue) {
- pendingJob = null;
- }
- performNextIO();
- return;
- }
- }
+ int errorCode;
+ //This must be in sync with QLowEnergyService::ServiceError
+ switch (status) {
+ case BluetoothGatt.GATT_SUCCESS:
+ errorCode = 0; break; // NoError
+ default:
+ errorCode = 3; break; // DescriptorWriteError
+ }
- boolean requestTimedOut = !handleForTimeout.compareAndSet(
- modifiedReadWriteHandle(foundHandle, IoJobType.Read), HANDLE_FOR_RESET);
- if (requestTimedOut) {
- Log.w(TAG, "Late descriptor read reply after timeout was hit for handle " +
- foundHandle);
- // Timeout has hit before this response -> ignore the response
- // no need to unlock pendingJob -> the timeout has done that already
- return;
- }
+ pendingJob = null;
- GattEntry entry = entries.get(foundHandle);
- final boolean isServiceDiscoveryRun = !entry.valueKnown;
- entry.valueKnown = true;
+ leDescriptorWritten(qtObject, handle+1, descriptor.getValue(), errorCode);
+ performNextIO();
+ }
- if (status == BluetoothGatt.GATT_SUCCESS) {
- //TODO avoid sending service and characteristic uuid -> handles should be sufficient
- leDescriptorRead(qtObject, descriptor.getCharacteristic().getService().getUuid().toString(),
- descriptor.getCharacteristic().getUuid().toString(), foundHandle + 1,
- descriptor.getUuid().toString(), descriptor.getValue());
- } else {
- if (isServiceDiscoveryRun) {
- // Cannot read but still advertise the fact that we found a descriptor
- // The value will be empty.
- Log.w(TAG, "onDescriptorRead during discovery error: " + status);
- Log.d(TAG, "Non-readable descriptor " + descriptor.getUuid() +
- " for characteristic " + descriptor.getCharacteristic().getUuid() +
- " for service " + descriptor.getCharacteristic().getService().getUuid());
- leDescriptorRead(qtObject, descriptor.getCharacteristic().getService().getUuid().toString(),
- descriptor.getCharacteristic().getUuid().toString(), foundHandle + 1,
- descriptor.getUuid().toString(), descriptor.getValue());
- } else {
- // This must be in sync with QLowEnergyService::DescriptorReadError
- final int descriptorReadError = 6;
- leServiceError(qtObject, foundHandle + 1, descriptorReadError);
- }
+ private synchronized void handleOnMtuChanged(android.bluetooth.BluetoothGatt gatt,
+ int mtu, int status)
+ {
+ int previousMtu = mSupportedMtu;
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ Log.w(TAG, "MTU changed to " + mtu);
+ mSupportedMtu = mtu;
+ } else {
+ Log.w(TAG, "MTU change error " + status + ". New MTU " + mtu);
+ mSupportedMtu = DEFAULT_MTU;
+ }
+ if (previousMtu != mSupportedMtu)
+ leMtuChanged(qtObject, mSupportedMtu);
+
+ boolean requestTimedOut = !handleForTimeout.compareAndSet(
+ modifiedReadWriteHandle(HANDLE_FOR_MTU_EXCHANGE, IoJobType.Mtu), HANDLE_FOR_RESET);
+ if (requestTimedOut) {
+ Log.w(TAG, "Late mtu reply after timeout was hit");
+ // Timeout has hit before this response -> ignore the response
+ // no need to unlock pendingJob -> the timeout has done that already
+ return;
+ }
- }
+ pendingJob = null;
- if (isServiceDiscoveryRun) {
- // last entry of pending service discovery run? ->send discovery finished state update
- GattEntry serviceEntry = entries.get(entry.associatedServiceHandle);
- if (serviceEntry.endHandle == foundHandle) {
- finishCurrentServiceDiscovery(entry.associatedServiceHandle);
- }
+ performNextIO();
+ }
- /* Some devices preset ClientCharacteristicConfiguration descriptors
- * to enable notifications out of the box. However the additional
- * BluetoothGatt.setCharacteristicNotification call prevents
- * automatic notifications from coming through. Hence we manually set them
- * up here.
- */
- if (descriptor.getUuid().compareTo(clientCharacteristicUuid) == 0) {
- byte[] bytearray = descriptor.getValue();
- final int value = (bytearray != null && bytearray.length > 0) ? bytearray[0] : 0;
- // notification or indication bit set?
- if ((value & 0x03) > 0) {
- Log.d(TAG, "Found descriptor with automatic notifications.");
- mBluetoothGatt.setCharacteristicNotification(
- descriptor.getCharacteristic(), true);
- }
- }
- }
+ /*************************************************************/
+ /* Service Discovery */
+ /*************************************************************/
- //unlock the queue for next item
- synchronized (readWriteQueue) {
- pendingJob = null;
- }
+ private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
- performNextIO();
+ public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
+ super.onConnectionStateChange(gatt, status, newState);
+ handleOnConnectionStateChange(gatt, status, newState);
}
- public void onDescriptorWrite(android.bluetooth.BluetoothGatt gatt,
- android.bluetooth.BluetoothGattDescriptor descriptor,
- int status)
+ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ super.onServicesDiscovered(gatt, status);
+ handleOnServicesDiscovered(gatt, status);
+
+ }
+
+ public void onCharacteristicRead(android.bluetooth.BluetoothGatt gatt,
+ android.bluetooth.BluetoothGattCharacteristic characteristic,
+ int status)
{
- if (status != BluetoothGatt.GATT_SUCCESS)
- Log.w(TAG, "onDescriptorWrite: error " + status);
-
- int handle = handleForDescriptor(descriptor);
-
- boolean requestTimedOut = !handleForTimeout.compareAndSet(
- modifiedReadWriteHandle(handle, IoJobType.Write), HANDLE_FOR_RESET);
- if (requestTimedOut) {
- Log.w(TAG, "Late descriptor write reply after timeout was hit for handle " +
- handle);
- // Timeout has hit before this response -> ignore the response
- // no need to unlock pendingJob -> the timeout has done that already
- return;
- }
+ super.onCharacteristicRead(gatt, characteristic, status);
+ handleOnCharacteristicRead(gatt, characteristic, status);
+ }
- int errorCode;
- //This must be in sync with QLowEnergyService::ServiceError
- switch (status) {
- case BluetoothGatt.GATT_SUCCESS:
- errorCode = 0; break; // NoError
- default:
- errorCode = 3; break; // DescriptorWriteError
- }
+ public void onCharacteristicWrite(android.bluetooth.BluetoothGatt gatt,
+ android.bluetooth.BluetoothGattCharacteristic characteristic,
+ int status)
+ {
+ super.onCharacteristicWrite(gatt, characteristic, status);
+ handleOnCharacteristicWrite(gatt, characteristic, status);
+ }
- synchronized (readWriteQueue) {
- pendingJob = null;
- }
+ public void onCharacteristicChanged(android.bluetooth.BluetoothGatt gatt,
+ android.bluetooth.BluetoothGattCharacteristic characteristic)
+ {
+ super.onCharacteristicChanged(gatt, characteristic);
+ handleOnCharacteristicChanged(gatt, characteristic);
+ }
- leDescriptorWritten(qtObject, handle+1, descriptor.getValue(), errorCode);
- performNextIO();
+ public void onDescriptorRead(android.bluetooth.BluetoothGatt gatt,
+ android.bluetooth.BluetoothGattDescriptor descriptor,
+ int status)
+ {
+ super.onDescriptorRead(gatt, descriptor, status);
+ handleOnDescriptorRead(gatt, descriptor, status);
+ }
+
+ public void onDescriptorWrite(android.bluetooth.BluetoothGatt gatt,
+ android.bluetooth.BluetoothGattDescriptor descriptor,
+ int status)
+ {
+ super.onDescriptorWrite(gatt, descriptor, status);
+ handleOnDescriptorWrite(gatt, descriptor, status);
}
//TODO Requires Android API 21 which is not available on CI yet.
// public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt gatt,
@@ -613,35 +702,13 @@ public class QtBluetoothLE {
// requires Android API v21
public void onMtuChanged(android.bluetooth.BluetoothGatt gatt, int mtu, int status)
{
- int previousMtu = mSupportedMtu;
- if (status == BluetoothGatt.GATT_SUCCESS) {
- Log.w(TAG, "MTU changed to " + mtu);
- mSupportedMtu = mtu;
- } else {
- Log.w(TAG, "MTU change error " + status + ". New MTU " + mtu);
- mSupportedMtu = DEFAULT_MTU;
- }
- if (previousMtu != mSupportedMtu)
- leMtuChanged(qtObject, mSupportedMtu);
-
- boolean requestTimedOut = !handleForTimeout.compareAndSet(
- modifiedReadWriteHandle(HANDLE_FOR_MTU_EXCHANGE, IoJobType.Mtu), HANDLE_FOR_RESET);
- if (requestTimedOut) {
- Log.w(TAG, "Late mtu reply after timeout was hit");
- // Timeout has hit before this response -> ignore the response
- // no need to unlock pendingJob -> the timeout has done that already
- return;
- }
-
- synchronized (readWriteQueue) {
- pendingJob = null;
- }
-
- performNextIO();
+ super.onMtuChanged(gatt, mtu, status);
+ handleOnMtuChanged(gatt, mtu, status);
}
};
- public int mtu() {
+ // This function is called from Qt thread
+ public synchronized int mtu() {
if (mSupportedMtu == -1) {
return DEFAULT_MTU;
} else {
@@ -649,7 +716,8 @@ public class QtBluetoothLE {
}
}
- public boolean connect() {
+ // This function is called from Qt thread
+ public synchronized boolean connect() {
BluetoothDevice mRemoteGattDevice;
try {
@@ -744,14 +812,16 @@ public class QtBluetoothLE {
return mBluetoothGatt != null;
}
- public void disconnect() {
+ // This function is called from Qt thread
+ public synchronized void disconnect() {
if (mBluetoothGatt == null)
return;
mBluetoothGatt.disconnect();
}
- public boolean discoverServices()
+ // This function is called from Qt thread
+ public synchronized boolean discoverServices()
{
return mBluetoothGatt != null && mBluetoothGatt.discoverServices();
}
@@ -799,7 +869,6 @@ public class QtBluetoothLE {
// index into array is equivalent to handle id
private final ArrayList<GattEntry> entries = new ArrayList<GattEntry>(100);
//backlog of to be discovered services
- // TODO remove
private final LinkedList<Integer> servicesToBeDiscovered = new LinkedList<Integer>();
@@ -888,6 +957,7 @@ public class QtBluetoothLE {
return -1;
}
+ // This function is called from Qt thread (indirectly)
private void populateHandles()
{
// We introduce the notion of artificial handles. While GATT handles
@@ -951,21 +1021,18 @@ public class QtBluetoothLE {
private void resetData()
{
- synchronized (this) {
- uuidToEntry.clear();
- entries.clear();
- servicesToBeDiscovered.clear();
- }
+ uuidToEntry.clear();
+ entries.clear();
+ servicesToBeDiscovered.clear();
// kill all timeout handlers
timeoutHandler.removeCallbacksAndMessages(null);
handleForTimeout.set(HANDLE_FOR_RESET);
- synchronized (readWriteQueue) {
- readWriteQueue.clear();
- }
+ readWriteQueue.clear();
}
+ // This function is called from Qt thread
public synchronized boolean discoverServiceDetails(String serviceUuid, boolean fullDiscovery)
{
try {
@@ -1022,9 +1089,9 @@ public class QtBluetoothLE {
/*
Returns the uuids of the services included by the given service. Otherwise returns null.
- Directly called from Qt.
+ This function is called from Qt thread
*/
- public String includedServices(String serviceUuid)
+ public synchronized String includedServices(String serviceUuid)
{
if (mBluetoothGatt == null)
return null;
@@ -1054,24 +1121,22 @@ public class QtBluetoothLE {
return builder.toString();
}
- //TODO function not yet used
- private void finishCurrentServiceDiscovery(int handleDiscoveredService)
+ private synchronized void finishCurrentServiceDiscovery(int handleDiscoveredService)
{
Log.w(TAG, "Finished current discovery for service handle " + handleDiscoveredService);
GattEntry discoveredService = entries.get(handleDiscoveredService);
discoveredService.valueKnown = true;
- synchronized (this) {
- try {
- servicesToBeDiscovered.removeFirst();
- } catch (NoSuchElementException ex) {
- Log.w(TAG, "Expected queued service but didn't find any");
- }
+ try {
+ servicesToBeDiscovered.removeFirst();
+ } catch (NoSuchElementException ex) {
+ Log.w(TAG, "Expected queued service but didn't find any");
}
leServiceDetailDiscoveryFinished(qtObject, discoveredService.service.getUuid().toString(),
handleDiscoveredService + 1, discoveredService.endHandle + 1);
}
+ // Executes under "this" client mutex
private boolean executeMtuExchange()
{
if (Build.VERSION.SDK_INT >= 21) {
@@ -1104,9 +1169,7 @@ public class QtBluetoothLE {
newJob.jobType = IoJobType.Mtu;
newJob.entry = null;
- synchronized (readWriteQueue) {
- readWriteQueue.add(newJob);
- }
+ readWriteQueue.add(newJob);
performNextIO();
}
@@ -1117,7 +1180,6 @@ public class QtBluetoothLE {
Adds all Gatt entries for the given service to the readWriteQueue to be discovered.
This function only ever adds read requests to the queue.
- //TODO function not yet used
*/
private void scheduleServiceDetailDiscovery(int serviceHandle, boolean fullDiscovery)
{
@@ -1130,42 +1192,37 @@ public class QtBluetoothLE {
return;
}
- synchronized (readWriteQueue) {
- // entire block inside mutex to ensure all service discovery jobs go in one after the other
- // ensures that serviceDiscovered() signal is sent when required
+ // serviceHandle + 1 -> ignore service handle itself
+ for (int i = serviceHandle + 1; i <= endHandle; i++) {
+ GattEntry entry = entries.get(i);
+ if (entry.type == GattEntryType.Service) {
+ // should not really happen unless endHandle is wrong
+ Log.w(TAG, "scheduleServiceDetailDiscovery: wrong endHandle");
+ return;
+ }
- // serviceHandle + 1 -> ignore service handle itself
- for (int i = serviceHandle + 1; i <= endHandle; i++) {
- GattEntry entry = entries.get(i);
-
- if (entry.type == GattEntryType.Service) {
- // should not really happen unless endHandle is wrong
- Log.w(TAG, "scheduleServiceDetailDiscovery: wrong endHandle");
- return;
- }
-
- ReadWriteJob newJob = new ReadWriteJob();
- newJob.entry = entry;
- if (fullDiscovery) {
- newJob.jobType = IoJobType.Read;
- } else {
- newJob.jobType = IoJobType.SkippedRead;
- }
-
- final boolean result = readWriteQueue.add(newJob);
- if (!result)
- Log.w(TAG, "Cannot add service discovery job for " + serviceEntry.service.getUuid()
- + " on item " + entry.type);
+ ReadWriteJob newJob = new ReadWriteJob();
+ newJob.entry = entry;
+ if (fullDiscovery) {
+ newJob.jobType = IoJobType.Read;
+ } else {
+ newJob.jobType = IoJobType.SkippedRead;
}
+
+ final boolean result = readWriteQueue.add(newJob);
+ if (!result)
+ Log.w(TAG, "Cannot add service discovery job for " + serviceEntry.service.getUuid()
+ + " on item " + entry.type);
}
}
/*************************************************************/
/* Write Characteristics */
+ /* This function is called from Qt thread */
/*************************************************************/
- public boolean writeCharacteristic(int charHandle, byte[] newValue,
+ public synchronized boolean writeCharacteristic(int charHandle, byte[] newValue,
int writeMode)
{
if (mBluetoothGatt == null)
@@ -1198,9 +1255,7 @@ public class QtBluetoothLE {
}
boolean result;
- synchronized (readWriteQueue) {
- result = readWriteQueue.add(newJob);
- }
+ result = readWriteQueue.add(newJob);
if (!result) {
Log.w(TAG, "Cannot add characteristic write request for " + charHandle + " to queue" );
@@ -1213,9 +1268,10 @@ public class QtBluetoothLE {
/*************************************************************/
/* Write Descriptors */
+ /* This function is called from Qt thread */
/*************************************************************/
- public boolean writeDescriptor(int descHandle, byte[] newValue)
+ public synchronized boolean writeDescriptor(int descHandle, byte[] newValue)
{
if (mBluetoothGatt == null)
return false;
@@ -1235,9 +1291,7 @@ public class QtBluetoothLE {
newJob.jobType = IoJobType.Write;
boolean result;
- synchronized (readWriteQueue) {
- result = readWriteQueue.add(newJob);
- }
+ result = readWriteQueue.add(newJob);
if (!result) {
Log.w(TAG, "Cannot add descriptor write request for " + descHandle + " to queue" );
@@ -1250,9 +1304,10 @@ public class QtBluetoothLE {
/*************************************************************/
/* Read Characteristics */
+ /* This function is called from Qt thread */
/*************************************************************/
- public boolean readCharacteristic(int charHandle)
+ public synchronized boolean readCharacteristic(int charHandle)
{
if (mBluetoothGatt == null)
return false;
@@ -1270,9 +1325,7 @@ public class QtBluetoothLE {
newJob.jobType = IoJobType.Read;
boolean result;
- synchronized (readWriteQueue) {
- result = readWriteQueue.add(newJob);
- }
+ result = readWriteQueue.add(newJob);
if (!result) {
Log.w(TAG, "Cannot add characteristic read request for " + charHandle + " to queue" );
@@ -1283,7 +1336,8 @@ public class QtBluetoothLE {
return true;
}
- public boolean readDescriptor(int descHandle)
+ // This function is called from Qt thread
+ public synchronized boolean readDescriptor(int descHandle)
{
if (mBluetoothGatt == null)
return false;
@@ -1301,9 +1355,7 @@ public class QtBluetoothLE {
newJob.jobType = IoJobType.Read;
boolean result;
- synchronized (readWriteQueue) {
- result = readWriteQueue.add(newJob);
- }
+ result = readWriteQueue.add(newJob);
if (!result) {
Log.w(TAG, "Cannot add descriptor read request for " + descHandle + " to queue" );
@@ -1317,12 +1369,10 @@ public class QtBluetoothLE {
// Called by TimeoutRunnable if the current I/O job timed out.
// By the time we reach this point the handleForTimeout counter has already been reset
// and the regular responses will be blocked off.
- private void interruptCurrentIO(int handle)
+ private synchronized void interruptCurrentIO(int handle)
{
//unlock the queue for next item
- synchronized (readWriteQueue) {
- pendingJob = null;
- }
+ pendingJob = null;
performNextIOThreaded();
@@ -1330,19 +1380,16 @@ public class QtBluetoothLE {
return;
try {
- synchronized (this) {
-
- GattEntry entry = entries.get(handle);
- if (entry == null)
- return;
- if (entry.valueKnown)
- return;
- entry.valueKnown = true;
+ GattEntry entry = entries.get(handle);
+ if (entry == null)
+ return;
+ if (entry.valueKnown)
+ return;
+ entry.valueKnown = true;
- GattEntry serviceEntry = entries.get(entry.associatedServiceHandle);
- if (serviceEntry != null && serviceEntry.endHandle == handle)
- finishCurrentServiceDiscovery(entry.associatedServiceHandle);
- }
+ GattEntry serviceEntry = entries.get(entry.associatedServiceHandle);
+ if (serviceEntry != null && serviceEntry.endHandle == handle)
+ finishCurrentServiceDiscovery(entry.associatedServiceHandle);
} catch (IndexOutOfBoundsException outOfBounds) {
Log.w(TAG, "interruptCurrentIO(): Unknown gatt entry, index: "
+ handle + " size: " + entries.size());
@@ -1372,7 +1419,7 @@ public class QtBluetoothLE {
cannot execute at the same time. The second write must happen after the
previous write has finished with on(Characteristic|Descriptor)Write().
*/
- private void performNextIO()
+ private synchronized void performNextIO()
{
if (mBluetoothGatt == null)
return;
@@ -1381,65 +1428,61 @@ public class QtBluetoothLE {
final ReadWriteJob nextJob;
int handle = HANDLE_FOR_RESET;
- synchronized (readWriteQueue) {
- if (readWriteQueue.isEmpty() || pendingJob != null)
- return;
-
- nextJob = readWriteQueue.remove();
- if (nextJob.jobType == IoJobType.Mtu) {
- handle = HANDLE_FOR_MTU_EXCHANGE; //mtu request is special case
- } else {
- switch (nextJob.entry.type) {
- case Characteristic:
- handle = handleForCharacteristic(nextJob.entry.characteristic);
- break;
- case Descriptor:
- handle = handleForDescriptor(nextJob.entry.descriptor);
- break;
- case CharacteristicValue:
- handle = nextJob.entry.endHandle;
- default:
- break;
- }
- }
+ if (readWriteQueue.isEmpty() || pendingJob != null)
+ return;
- // timeout handler and handleForTimeout atomic must be setup before
- // executing the request. Sometimes the callback is quicker than executing the
- // remainder of this function. Therefore enable the atomic early such that
- // callback handlers start hanging in the readWriteQueue sync block which
- // we are still occupying here.
- timeoutHandler.removeCallbacksAndMessages(null); // remove any timeout handlers
- handleForTimeout.set(modifiedReadWriteHandle(handle, nextJob.jobType));
-
- switch (nextJob.jobType) {
- case Read:
- skip = executeReadJob(nextJob);
- break;
- case SkippedRead:
- skip = true;
+ nextJob = readWriteQueue.remove();
+ if (nextJob.jobType == IoJobType.Mtu) {
+ handle = HANDLE_FOR_MTU_EXCHANGE; //mtu request is special case
+ } else {
+ switch (nextJob.entry.type) {
+ case Characteristic:
+ handle = handleForCharacteristic(nextJob.entry.characteristic);
break;
- case Write:
- skip = executeWriteJob(nextJob);
+ case Descriptor:
+ handle = handleForDescriptor(nextJob.entry.descriptor);
break;
- case Mtu:
- skip = executeMtuExchange();
+ case CharacteristicValue:
+ handle = nextJob.entry.endHandle;
+ default:
break;
}
+ }
- if (skip) {
- handleForTimeout.set(HANDLE_FOR_RESET); // not a pending call -> release atomic
- } else {
- pendingJob = nextJob;
- timeoutHandler.postDelayed(new TimeoutRunnable(
- modifiedReadWriteHandle(handle, nextJob.jobType)), RUNNABLE_TIMEOUT);
- }
+ // timeout handler and handleForTimeout atomic must be setup before
+ // executing the request. Sometimes the callback is quicker than executing the
+ // remainder of this function. Therefore enable the atomic early
+ timeoutHandler.removeCallbacksAndMessages(null); // remove any timeout handlers
+ handleForTimeout.set(modifiedReadWriteHandle(handle, nextJob.jobType));
- if (nextJob.jobType != IoJobType.Mtu) {
- Log.w(TAG, "Performing queued job, handle: " + handle + " " + nextJob.jobType + " (" +
- (nextJob.requestedWriteType == BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE) +
- ") ValueKnown: " + nextJob.entry.valueKnown + " Skipping: " + skip +
- " " + nextJob.entry.type);
- }
+ switch (nextJob.jobType) {
+ case Read:
+ skip = executeReadJob(nextJob);
+ break;
+ case SkippedRead:
+ skip = true;
+ break;
+ case Write:
+ skip = executeWriteJob(nextJob);
+ break;
+ case Mtu:
+ skip = executeMtuExchange();
+ break;
+ }
+
+ if (skip) {
+ handleForTimeout.set(HANDLE_FOR_RESET); // not a pending call -> release atomic
+ } else {
+ pendingJob = nextJob;
+ timeoutHandler.postDelayed(new TimeoutRunnable(
+ modifiedReadWriteHandle(handle, nextJob.jobType)), RUNNABLE_TIMEOUT);
+ }
+
+ if (nextJob.jobType != IoJobType.Mtu) {
+ Log.w(TAG, "Performing queued job, handle: " + handle + " " + nextJob.jobType + " (" +
+ (nextJob.requestedWriteType == BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE) +
+ ") ValueKnown: " + nextJob.entry.valueKnown + " Skipping: " + skip +
+ " " + nextJob.entry.type);
}
GattEntry entry = nextJob.entry;
@@ -1491,15 +1534,13 @@ public class QtBluetoothLE {
}
// last entry of current discovery run?
- synchronized (this) {
- try {
- GattEntry serviceEntry = entries.get(entry.associatedServiceHandle);
- if (serviceEntry.endHandle == handle)
- finishCurrentServiceDiscovery(entry.associatedServiceHandle);
- } catch (IndexOutOfBoundsException outOfBounds) {
- Log.w(TAG, "performNextIO(): Unknown service for entry, index: "
- + entry.associatedServiceHandle + " size: " + entries.size());
- }
+ try {
+ GattEntry serviceEntry = entries.get(entry.associatedServiceHandle);
+ if (serviceEntry.endHandle == handle)
+ finishCurrentServiceDiscovery(entry.associatedServiceHandle);
+ } catch (IndexOutOfBoundsException outOfBounds) {
+ Log.w(TAG, "performNextIO(): Unknown service for entry, index: "
+ + entry.associatedServiceHandle + " size: " + entries.size());
}
} else {
int errorCode = 0;
@@ -1531,7 +1572,6 @@ public class QtBluetoothLE {
}
}
- // Runs inside the Mutex on readWriteQueue.
// Returns true if nextJob should be skipped.
private boolean executeWriteJob(ReadWriteJob nextJob)
{
@@ -1600,7 +1640,6 @@ public class QtBluetoothLE {
return false;
}
- // Runs inside the Mutex on readWriteQueue.
// Returns true if nextJob should be skipped.
private boolean executeReadJob(ReadWriteJob nextJob)
{
@@ -1678,8 +1717,8 @@ public class QtBluetoothLE {
return modifiedHandle;
}
- // Directly called from public Qt API
- public boolean requestConnectionUpdatePriority(double minimalInterval)
+ // This function is called from Qt thread
+ public synchronized boolean requestConnectionUpdatePriority(double minimalInterval)
{
if (mBluetoothGatt == null)
return false;