diff options
author | Alex Blasche <alexander.blasche@qt.io> | 2017-07-26 14:18:19 +0200 |
---|---|---|
committer | Alex Blasche <alexander.blasche@qt.io> | 2017-07-26 14:18:27 +0200 |
commit | 4849c1e95bb9521d08a7f8589c16c3b5849a7712 (patch) | |
tree | c31389410ae92cfb656de528adad766c6f512978 | |
parent | 892d50020da29807d328b07d7568990e803c36f0 (diff) | |
parent | 96590b7d608ef25c21a2025d368b50b56b958364 (diff) |
Merge remote-tracking branch 'gerrit/5.9' into dev
Change-Id: Ida3681a873698f22c28bc60230ca0c49d2a9d25f
5 files changed, 142 insertions, 43 deletions
diff --git a/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java index 9571ffdc..1b527ae3 100644 --- a/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java +++ b/src/android/bluetooth/src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java @@ -48,6 +48,7 @@ import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothProfile; import android.content.Context; +import android.os.Build; import android.os.Handler; import android.os.Looper; import android.util.Log; @@ -71,13 +72,21 @@ public class QtBluetoothLE { private BluetoothGatt mBluetoothGatt = null; private String mRemoteGattAddress; private final UUID clientCharacteristicUuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); + private final int MAX_MTU = 512; + private final int DEFAULT_MTU = 23; + private int mSupportedMtu = -1; /* * The atomic synchronizes the timeoutRunnable thread and the response thread for the pending * I/O job. Whichever thread comes first will pass the atomic gate. The other thread is * cut short. */ - private AtomicInteger handleForTimeout = new AtomicInteger(-1); // -1 implies not running + // handle values above zero are for regular handle specific read/write requests + // handle values below zero are reserved for handle-independent requests + private int HANDLE_FOR_RESET = -1; + private int HANDLE_FOR_MTU_EXCHANGE = -2; + private AtomicInteger handleForTimeout = new AtomicInteger(HANDLE_FOR_RESET); // implies not running by default + private final int RUNNABLE_TIMEOUT = 3000; // 3 seconds private final Handler timeoutHandler = new Handler(Looper.getMainLooper()); @@ -85,14 +94,18 @@ public class QtBluetoothLE { public TimeoutRunnable(int handle) { pendingJobHandle = handle; } @Override public void run() { - boolean timeoutStillValid = handleForTimeout.compareAndSet(pendingJobHandle, -1); + boolean timeoutStillValid = handleForTimeout.compareAndSet(pendingJobHandle, HANDLE_FOR_RESET); if (timeoutStillValid) { Log.w(TAG, "****** Timeout for request on handle " + (pendingJobHandle & 0xffff)); - Log.w(TAG, "****** Looks like the characteristic or descriptor does NOT act in " + + Log.w(TAG, "****** Looks like the peripheral does NOT act in " + "accordance to Bluetooth 4.x spec."); Log.w(TAG, "****** Please check server implementation. Continuing under " + "reservation."); - interruptCurrentIO(pendingJobHandle & 0xffff); + + if (pendingJobHandle > HANDLE_FOR_RESET) + interruptCurrentIO(pendingJobHandle & 0xffff); + else if (pendingJobHandle < HANDLE_FOR_RESET) + interruptCurrentIO(pendingJobHandle); } } @@ -223,6 +236,8 @@ public class QtBluetoothLE { errorCode = status; break; //TODO deal with all errors } leServicesDiscovered(qtObject, errorCode, builder.toString()); + + scheduleMtuExchange(); } public void onCharacteristicRead(android.bluetooth.BluetoothGatt gatt, @@ -247,7 +262,7 @@ public class QtBluetoothLE { } boolean requestTimedOut = !handleForTimeout.compareAndSet( - modifiedReadWriteHandle(foundHandle, IoJobType.Read), -1); + 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 @@ -311,7 +326,7 @@ public class QtBluetoothLE { } boolean requestTimedOut = !handleForTimeout.compareAndSet( - modifiedReadWriteHandle(handle, IoJobType.Write), -1); + 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 @@ -368,7 +383,7 @@ public class QtBluetoothLE { } boolean requestTimedOut = !handleForTimeout.compareAndSet( - modifiedReadWriteHandle(foundHandle, IoJobType.Read), -1); + modifiedReadWriteHandle(foundHandle, IoJobType.Read), HANDLE_FOR_RESET); if (requestTimedOut) { Log.w(TAG, "Late descriptor read reply after timeout was hit for handle " + foundHandle); @@ -441,7 +456,7 @@ public class QtBluetoothLE { int handle = handleForDescriptor(descriptor); boolean requestTimedOut = !handleForTimeout.compareAndSet( - modifiedReadWriteHandle(handle, IoJobType.Write), -1); + modifiedReadWriteHandle(handle, IoJobType.Write), HANDLE_FOR_RESET); if (requestTimedOut) { Log.w(TAG, "Late descriptor write reply after timeout was hit for handle " + handle); @@ -477,6 +492,32 @@ public class QtBluetoothLE { // System.out.println("onReadRemoteRssi"); // } + // requires Android API v21 + public void onMtuChanged(android.bluetooth.BluetoothGatt gatt, int mtu, int status) + { + 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; + } + + 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 ioJobPending -> the timeout has done that already + return; + } + + synchronized (readWriteQueue) { + ioJobPending = false; + } + + performNextIO(); + } }; @@ -554,7 +595,7 @@ public class QtBluetoothLE { private enum IoJobType { - Read, Write + Read, Write, Mtu } private class ReadWriteJob @@ -730,7 +771,7 @@ public class QtBluetoothLE { // kill all timeout handlers timeoutHandler.removeCallbacksAndMessages(null); - handleForTimeout.set(-1); + handleForTimeout.set(HANDLE_FOR_RESET); synchronized (readWriteQueue) { readWriteQueue.clear(); @@ -841,6 +882,42 @@ public class QtBluetoothLE { handleDiscoveredService + 1, discoveredService.endHandle + 1); } + private boolean executeMtuExchange() + { + if (Build.VERSION.SDK_INT >= 21) { + try { + Method mtuMethod = mBluetoothGatt.getClass().getDeclaredMethod("requestMtu", int.class); + if (mtuMethod != null) { + Boolean success = (Boolean) mtuMethod.invoke(mBluetoothGatt, MAX_MTU); + if (success.booleanValue()) { + Log.w(TAG, "MTU change initiated"); + return false; + } else { + Log.w(TAG, "MTU change request failed"); + } + } + } catch (Exception ex) {} + } + + Log.w(TAG, "Assuming default MTU value of 23 bytes"); + + mSupportedMtu = DEFAULT_MTU; + return true; + } + + private void scheduleMtuExchange() + { + ReadWriteJob newJob = new ReadWriteJob(); + newJob.jobType = IoJobType.Mtu; + newJob.entry = null; + + synchronized (readWriteQueue) { + readWriteQueue.add(newJob); + } + + performNextIO(); + } + /* Internal Helper function for discoverServiceDetails() @@ -1061,6 +1138,9 @@ public class QtBluetoothLE { performNextIO(); + if (handle == HANDLE_FOR_MTU_EXCHANGE) + return; + GattEntry entry = entries.get(handle); if (entry == null) return; @@ -1085,24 +1165,28 @@ public class QtBluetoothLE { boolean skip = false; final ReadWriteJob nextJob; - int handle = -1; + int handle = HANDLE_FOR_RESET; synchronized (readWriteQueue) { if (readWriteQueue.isEmpty() || ioJobPending) return; nextJob = readWriteQueue.remove(); - 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 (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; + } } // timeout handler and handleForTimeout atomic must be setup before @@ -1113,23 +1197,32 @@ public class QtBluetoothLE { timeoutHandler.removeCallbacksAndMessages(null); // remove any timeout handlers handleForTimeout.set(modifiedReadWriteHandle(handle, nextJob.jobType)); - if (nextJob.jobType == IoJobType.Read) - skip = executeReadJob(nextJob); - else - skip = executeWriteJob(nextJob); + switch (nextJob.jobType) { + case Read: + skip = executeReadJob(nextJob); + break; + case Write: + skip = executeWriteJob(nextJob); + break; + case Mtu: + skip = executeMtuExchange(); + break; + } if (skip) { - handleForTimeout.set(-1); // not a pending call -> release atomic + handleForTimeout.set(HANDLE_FOR_RESET); // not a pending call -> release atomic } else { ioJobPending = true; timeoutHandler.postDelayed(new TimeoutRunnable( modifiedReadWriteHandle(handle, nextJob.jobType)), RUNNABLE_TIMEOUT); } - Log.w(TAG, "Performing queued job, handle: " + 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); + } } GattEntry entry = nextJob.entry; @@ -1142,7 +1235,7 @@ public class QtBluetoothLE { we have to report an error back to Qt. The error report is not required during the initial service discovery though. */ - if (handle != -1) { + if (handle > HANDLE_FOR_RESET) { // during service discovery we do not report error but emit characteristicRead() // any other time a failure emits serviceError() signal @@ -1171,15 +1264,16 @@ public class QtBluetoothLE { break; case CharacteristicValue: // for more details see scheduleServiceDetailDiscovery(int) - // ignore and continue unless last entry - GattEntry serviceEntry = entries.get(entry.associatedServiceHandle); - if (serviceEntry.endHandle == handle) - finishCurrentServiceDiscovery(entry.associatedServiceHandle); break; case Service: Log.w(TAG, "Scheduling of Service Gatt entry for service discovery should never happen."); break; } + + // last entry of current discovery run? + GattEntry serviceEntry = entries.get(entry.associatedServiceHandle); + if (serviceEntry.endHandle == handle) + finishCurrentServiceDiscovery(entry.associatedServiceHandle); } else { int errorCode = 0; @@ -1332,6 +1426,9 @@ public class QtBluetoothLE { case Read: modifiedHandle = (modifiedHandle | 0x00020000); break; + case Mtu: + modifiedHandle = HANDLE_FOR_MTU_EXCHANGE; + break; } return modifiedHandle; diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index 13d85a94..243d7fd2 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -78,12 +78,12 @@ class QDBusVariant; QT_END_NAMESPACE #endif +QT_BEGIN_NAMESPACE + #ifdef QT_WINRT_BLUETOOTH class QWinRTBluetoothDeviceDiscoveryWorker; #endif -QT_BEGIN_NAMESPACE - class QBluetoothDeviceDiscoveryAgentPrivate #if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) : public QObject diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.h b/src/bluetooth/qbluetoothservicediscoveryagent_p.h index 9a8da5e3..c4ea20a9 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h @@ -72,10 +72,6 @@ class QXmlStreamReader; QT_END_NAMESPACE #endif -#ifdef QT_WINRT_BLUETOOTH -class QWinRTBluetoothServiceDiscoveryWorker; -#endif - QT_BEGIN_NAMESPACE class QBluetoothDeviceDiscoveryAgent; @@ -86,6 +82,10 @@ class LocalDeviceBroadcastReceiver; #include <QtBluetooth/QBluetoothLocalDevice> #endif +#ifdef QT_WINRT_BLUETOOTH +class QWinRTBluetoothServiceDiscoveryWorker; +#endif + class QBluetoothServiceDiscoveryAgentPrivate #if defined QT_WINRT_BLUETOOTH : public QObject diff --git a/src/bluetooth/qbluetoothsocket_p.h b/src/bluetooth/qbluetoothsocket_p.h index 9aabf660..907acbe2 100644 --- a/src/bluetooth/qbluetoothsocket_p.h +++ b/src/bluetooth/qbluetoothsocket_p.h @@ -77,8 +77,6 @@ namespace ABI { } } } - -class SocketWorker; #endif // QT_WINRT_BLUETOOTH #ifndef QPRIVATELINEARBUFFER_BUFFERSIZE @@ -92,6 +90,10 @@ QT_FORWARD_DECLARE_CLASS(QSocketNotifier) QT_BEGIN_NAMESPACE +#ifdef QT_WINRT_BLUETOOTH +class SocketWorker; +#endif + class QBluetoothServiceDiscoveryAgent; class QSocketServerPrivate diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h index 3f220fe6..6e866144 100644 --- a/src/bluetooth/qlowenergycontroller_p.h +++ b/src/bluetooth/qlowenergycontroller_p.h @@ -85,8 +85,6 @@ QT_END_NAMESPACE #elif defined(QT_WINRT_BLUETOOTH) #include <wrl.h> #include <windows.devices.bluetooth.h> - -class QWinRTLowEnergyServiceHandler; #endif #include <functional> @@ -103,6 +101,8 @@ class QSocketNotifier; class RemoteDeviceManager; #elif defined(QT_ANDROID_BLUETOOTH) class LowEnergyNotificationHub; +#elif defined(QT_WINRT_BLUETOOTH) +class QWinRTLowEnergyServiceHandler; #endif extern void registerQLowEnergyControllerMetaType(); |