From 005cef90c6e06ef419dcec8f4619402677470301 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Tue, 5 Feb 2019 00:29:14 +0100 Subject: Fully qualify the parameter in QNearFieldManager::adapterStateChanged The signal parameter needs to be fully qualfied so that it can be correctly matched when using the macros. This can cause a breakage if an application was connecting using the non fully qualified type, which can be the case if invokeMethod() or similar is used. However the chances of this are incredibly slim, and it is worth the risk to ensure normal connections will work correctly. [ChangeLog][Important Behavior Changes][QtNFC][QNearFieldManager] adapterStateChanged now has a fully qualified parameter - QNearFieldManager::AdapterState. This can cause a problem if the connection/method invocation was done with just AdapterState as the parameter type, in which case these need to be changed to use QNearFieldManager::AdapaterState instead. Change-Id: I7311ff4cb15b67089ceb5917961ee979c9566f28 Reviewed-by: Alex Blasche --- src/nfc/qnearfieldmanager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nfc/qnearfieldmanager.h b/src/nfc/qnearfieldmanager.h index 500b9631..22506e7e 100644 --- a/src/nfc/qnearfieldmanager.h +++ b/src/nfc/qnearfieldmanager.h @@ -99,7 +99,7 @@ public: bool unregisterNdefMessageHandler(int handlerId); Q_SIGNALS: - void adapterStateChanged(AdapterState state); + void adapterStateChanged(QNearFieldManager::AdapterState state); void targetDetected(QNearFieldTarget *target); void targetLost(QNearFieldTarget *target); -- cgit v1.2.3 From 80a1346a139343b221d49b7c7b7fabcd55669a65 Mon Sep 17 00:00:00 2001 From: Nico Vertriest Date: Thu, 7 Feb 2019 13:57:05 +0100 Subject: Doc: Fix link error to \macos Change-Id: I82d691667232bebf7fed2cf9b24b234289e6d258 Reviewed-by: Alex Blasche --- src/bluetooth/doc/src/bluetooth-index.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bluetooth/doc/src/bluetooth-index.qdoc b/src/bluetooth/doc/src/bluetooth-index.qdoc index 2a4f72bc..a0e2a048 100644 --- a/src/bluetooth/doc/src/bluetooth-index.qdoc +++ b/src/bluetooth/doc/src/bluetooth-index.qdoc @@ -41,7 +41,7 @@ Currently, the API is supported on the following platforms: \li \l {Qt for Android}{Android} \li \l {Qt for iOS}{iOS} \li \l {Qt for Linux/X11}{Linux (BlueZ 4.x/5.x)} - \li \l {Qt for OS X}{macOS} + \li \l \macos \li \l {Qt for WinRT}{WinRT} \li \l {Qt for Windows}{Win32} \row -- cgit v1.2.3 From 7accd34495a8269f9b335446ae779bd56cd4c4c0 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Thu, 7 Feb 2019 16:40:53 +0100 Subject: Bluetooth LE scan - fix a crash (CoreBluetooth) 1. When the central's state changes to powered off, we emit PoweredOffError and QBluetoothDeviceDiscoveryAgent deletes Obj-C instance - a delegate for CBCentralManager. But we can still have GDC timer waiting in a queue and triggering the crash while using a dangling pointer. So we have to properly cancel the timer. 2. CoreBluetooth under debugger warns about API misuse - calling stopScan, apparently, is not allowed if CBCentralManager is in a state different from 'powered on'. Change-Id: Ib218105735995dc7988751fa04a6c76cab10cba8 Fixes: QTBUG-73140 Reviewed-by: Alex Blasche --- src/bluetooth/osx/osxbtledeviceinquiry.mm | 87 ++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/src/bluetooth/osx/osxbtledeviceinquiry.mm b/src/bluetooth/osx/osxbtledeviceinquiry.mm index ccf1c279..df0c26ad 100644 --- a/src/bluetooth/osx/osxbtledeviceinquiry.mm +++ b/src/bluetooth/osx/osxbtledeviceinquiry.mm @@ -121,6 +121,11 @@ QT_END_NAMESPACE QT_USE_NAMESPACE +@interface QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry)(PrivateAPI) +- (void)stopScanSafe; +- (void)stopNotifier; +@end + @implementation QT_MANGLE_NAMESPACE(OSXBTLEDeviceInquiry) { LECBManagerNotifier *notifier; @@ -147,17 +152,10 @@ QT_USE_NAMESPACE - (void)dealloc { - if (manager) { - [manager setDelegate:nil]; - if (internalState == InquiryActive) - [manager stopScan]; - } - - if (notifier) { - notifier->disconnect(); - notifier->deleteLater(); - } - + [self stopScanSafe]; + [manager setDelegate:nil]; + [elapsedTimer cancelTimer]; + [self stopNotifier]; [super dealloc]; } @@ -166,7 +164,7 @@ QT_USE_NAMESPACE Q_UNUSED(sender) if (internalState == InquiryActive) { - [manager stopScan]; + [self stopScanSafe]; [manager setDelegate:nil]; internalState = InquiryFinished; Q_ASSERT(notifier); @@ -228,7 +226,7 @@ QT_USE_NAMESPACE } else if (state == CBCentralManagerStateUnsupported || state == CBCentralManagerStateUnauthorized) { #endif if (internalState == InquiryActive) { - [manager stopScan]; + [self stopScanSafe]; // Not sure how this is possible at all, // probably, can never happen. internalState = ErrorPoweredOff; @@ -244,8 +242,9 @@ QT_USE_NAMESPACE #else } else if (state == CBCentralManagerStatePoweredOff) { #endif + +#ifndef Q_OS_MACOS if (internalState == InquiryStarting) { -#ifndef Q_OS_OSX // On iOS a user can see at this point an alert asking to // enable Bluetooth in the "Settings" app. If a user does so, // we'll receive 'PoweredOn' state update later. @@ -254,17 +253,19 @@ QT_USE_NAMESPACE elapsedTimer.resetWithoutRetain([[GCDTimerObjC alloc] initWithDelegate:self]); [elapsedTimer startWithTimeout:powerOffTimeoutMS step:300]; return; + } #else Q_UNUSED(powerOffTimeoutMS) -#endif - internalState = ErrorPoweredOff; - emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError); - } else { - [manager stopScan]; - emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError); - } - +#endif // Q_OS_MACOS + [elapsedTimer cancelTimer]; + [self stopScanSafe]; [manager setDelegate:nil]; + internalState = ErrorPoweredOff; + // On macOS we report PoweredOffError and our C++ owner will delete us + // (here we're kwnon as 'self'). Connection is Qt::QueuedConnection so we + // are apparently safe to call -stopNotifier after the signal. + emit notifier->CBManagerError(QBluetoothDeviceDiscoveryAgent::PoweredOffError); + [self stopNotifier]; } else { // The following two states we ignore (from Apple's docs): //" @@ -281,19 +282,45 @@ QT_USE_NAMESPACE #pragma clang diagnostic pop } -- (void)stop +- (void)stopScanSafe { - if (internalState == InquiryActive) - [manager stopScan]; + // CoreBluetooth warns about API misused if we call stopScan in a state + // other than powered on. Hence this 'Safe' ... + if (!manager) + return; - [elapsedTimer cancelTimer]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability-new" + + if (internalState == InquiryActive) { + const auto state = manager.data().state; + #if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_10_0) || QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13) + if (state == CBManagerStatePoweredOn) + #else + if (state == CBCentralManagerStatePoweredOn) + #endif + [manager stopScan]; + } + +#pragma clang diagnostic pop +} + +- (void)stopNotifier +{ + if (notifier) { + notifier->disconnect(); + notifier->deleteLater(); + notifier = nullptr; + } +} +- (void)stop +{ + [self stopScanSafe]; [manager setDelegate:nil]; + [elapsedTimer cancelTimer]; + [self stopNotifier]; internalState = InquiryCancelled; - - notifier->disconnect(); - notifier->deleteLater(); - notifier = nullptr; } - (void)centralManager:(CBCentralManager *)central -- cgit v1.2.3 From d41eaba6fb19350d51e6487440af13ac4320f46e Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Fri, 8 Feb 2019 12:53:58 +0100 Subject: Fix potential bug in osxbtcentralmanager Similar to the problem found in osxbtleinquiry - 'timers' waiting to fire on "qt-LE-queue" know nothing about us, potentially deleting the delegate object in response to Bluetooth switched off. Task-number: QTBUG-73140 Change-Id: I1a39a1ca02d019f90a1b4214cdbbb76e26b9eea0 Reviewed-by: Alex Blasche --- src/bluetooth/osx/osxbtcentralmanager.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bluetooth/osx/osxbtcentralmanager.mm b/src/bluetooth/osx/osxbtcentralmanager.mm index cadabbaf..41713909 100644 --- a/src/bluetooth/osx/osxbtcentralmanager.mm +++ b/src/bluetooth/osx/osxbtcentralmanager.mm @@ -1245,6 +1245,7 @@ QT_USE_NAMESPACE if (notifier) emit notifier->CBManagerError(QLowEnergyController::InvalidBluetoothAdapterError); } + [self stopWatchers]; return; } @@ -1266,6 +1267,7 @@ QT_USE_NAMESPACE if (notifier) emit notifier->CBManagerError(QLowEnergyController::InvalidBluetoothAdapterError); } + [self stopWatchers]; return; } @@ -1280,7 +1282,7 @@ QT_USE_NAMESPACE } } else { // We actually handled all known states, but .. Core Bluetooth can change? - Q_ASSERT_X(0, Q_FUNC_INFO, "invalid centra's state"); + Q_ASSERT_X(0, Q_FUNC_INFO, "invalid central's state"); } #pragma clang diagnostic pop -- cgit v1.2.3 From 862c766f0896314bf850c5573bb39788f211c1e1 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Fri, 8 Feb 2019 10:17:07 +0100 Subject: Add Android exclamation to QBluetoothServiceDiscoveryAgent ctor docs Passing the local address does not work because the passed address cannot be matched against the local adapter address. For privacy reasons this is not possible anymore since Android 6.0. Change-Id: Iacc632dda4af83265bdc3a062c0eba6c31cfa596 Reviewed-by: Timur Pocheptsov --- src/bluetooth/qbluetoothservicediscoveryagent.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.cpp b/src/bluetooth/qbluetoothservicediscoveryagent.cpp index 390b9f74..0f6fdc63 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent.cpp @@ -169,6 +169,11 @@ QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(QObject *parent \note On WinRT the passed adapter address will be ignored. + \note On Android passing any \a deviceAdapter address is meaningless as Android 6.0 or later does not publish + the local Bluetooth address anymore. Subsequently, the passed adapter address can never be matched + against the local adapter address. Therefore the subsequent call to \l start() will always trigger + \l InvalidBluetoothAdapterError. + \sa error() */ QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(const QBluetoothAddress &deviceAdapter, QObject *parent) -- cgit v1.2.3 From 1606ccb76ba72990df652fbd7f01d709ae20b63c Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Fri, 8 Feb 2019 09:51:19 +0100 Subject: Fix unit test failure on Android It fixes the following failure FAIL! : tst_QBluetoothServiceDiscoveryAgent::tst_invalidBtAddress() Compared values are not the same Actual (discoveryAgent->error()) : InvalidBluetoothAdapterError Expected (QBluetoothServiceDiscoveryAgent::NoError): NoError This behavior was caused by the fact that the InvalidBluetoothAdapterError was already triggered in the QBluetoothServiceDiscoveryAgent ctor whereas convention for this class states that the error is set when QBluetoothServiceDiscoveryAgent::start() is called. The fix detects whether the requested local adapter address matches the existing local adapter address. If there is no match Invalid adapter error is thrown. Task-number: QTBUG-73571 Change-Id: I3216e1609820a66893768b33f0fc695fbad6966a Reviewed-by: Timur Pocheptsov --- .../qbluetoothservicediscoveryagent_android.cpp | 42 ++++++++++++++++------ 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp index ce2911d3..ddc53421 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp @@ -55,21 +55,33 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID) QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate( - QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &/*deviceAdapter*/) + QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &deviceAdapter) : error(QBluetoothServiceDiscoveryAgent::NoError), + m_deviceAdapterAddress(deviceAdapter), state(Inactive), mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery), singleDevice(false), q_ptr(qp) { - QList devices = QBluetoothLocalDevice::allDevices(); - Q_ASSERT(devices.count() <= 1); //Android only supports one device at the moment - - if (devices.isEmpty()) { - error = QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError; - errorString = QBluetoothServiceDiscoveryAgent::tr("Invalid Bluetooth adapter address"); - return; + // If a specific adapter address is requested we need to check it matches + // the current local adapter. If it does not match we emit + // InvalidBluetoothAdapterError when calling start() + + bool createAdapter = true; + if (!deviceAdapter.isNull()) { + const QList devices = QBluetoothLocalDevice::allDevices(); + if (devices.isEmpty()) { + createAdapter = false; + } else { + auto match = [deviceAdapter](const QBluetoothHostInfo& info) { + return info.address() == deviceAdapter; + }; + + auto result = std::find_if(devices.begin(), devices.end(), match); + if (result == devices.end()) + createAdapter = false; + } } if (QtAndroidPrivate::androidSdkVersion() < 15) @@ -84,7 +96,8 @@ QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate( The logic below must change once there is more than one adapter. */ - btAdapter = QAndroidJniObject::callStaticObjectMethod("android/bluetooth/BluetoothAdapter", + if (createAdapter) + btAdapter = QAndroidJniObject::callStaticObjectMethod("android/bluetooth/BluetoothAdapter", "getDefaultAdapter", "()Landroid/bluetooth/BluetoothAdapter;"); if (!btAdapter.isValid()) @@ -110,8 +123,15 @@ void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &addr Q_Q(QBluetoothServiceDiscoveryAgent); if (!btAdapter.isValid()) { - error = QBluetoothServiceDiscoveryAgent::UnknownError; - errorString = QBluetoothServiceDiscoveryAgent::tr("Platform does not support Bluetooth"); + if (m_deviceAdapterAddress.isNull()) { + error = QBluetoothServiceDiscoveryAgent::UnknownError; + errorString = QBluetoothServiceDiscoveryAgent::tr("Platform does not support Bluetooth"); + } else { + // specific adapter was requested which does not match the locally + // existing adapter + error = QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError; + errorString = QBluetoothServiceDiscoveryAgent::tr("Invalid Bluetooth adapter address"); + } //abort any outstanding discoveries discoveredDevices.clear(); -- cgit v1.2.3 From 8af2bea29a4aec10d68f022520d4abca7320ebb1 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Mon, 21 Jan 2019 15:08:57 +0100 Subject: Add QLowEnergyController::createCentral() with localDevice overload The QLowEnergyController ctor with localDevice overload was obsoleted but the new API did not provide an equivalent createCentral() overload. This patch fixes the problem. Fixes: QTBUG-63019 Change-Id: I4c5a249bbf20c78da5507fecc874daa67e3dce46 Reviewed-by: Timur Pocheptsov --- src/bluetooth/qlowenergycontroller.cpp | 23 +++++++++++++++++++++++ src/bluetooth/qlowenergycontroller.h | 5 ++++- src/bluetooth/qlowenergycontroller_bluezdbus.cpp | 10 +++++++--- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller.cpp b/src/bluetooth/qlowenergycontroller.cpp index 8fc044fb..d8aa00d7 100644 --- a/src/bluetooth/qlowenergycontroller.cpp +++ b/src/bluetooth/qlowenergycontroller.cpp @@ -432,6 +432,29 @@ QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothDevice return new QLowEnergyController(remoteDevice, parent); } +/*! + Returns a new instance of this class with \a parent. + + The \a remoteDevice must contain the address of the remote Bluetooth Low + Energy device to which this object should attempt to connect later on. + + The connection is established via \a localDevice. If \a localDevice is invalid, + the local default device is automatically selected. If \a localDevice specifies + a local device that is not a local Bluetooth adapter, \l error() is set to + \l InvalidBluetoothAdapterError once \l connectToDevice() is called. + + Note that specifying the local device to be used for the connection is only + possible when using BlueZ. All other platforms do not support this feature. + + \since 5.13 + */ +QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothAddress &remoteDevice, + const QBluetoothAddress &localDevice, + QObject *parent) +{ + return new QLowEnergyController(remoteDevice, localDevice, parent); +} + /*! Returns a new object of this class that is in the \l PeripheralRole and has the diff --git a/src/bluetooth/qlowenergycontroller.h b/src/bluetooth/qlowenergycontroller.h index 9fe46fe5..5498d4a5 100644 --- a/src/bluetooth/qlowenergycontroller.h +++ b/src/bluetooth/qlowenergycontroller.h @@ -93,13 +93,16 @@ public: explicit QLowEnergyController(const QBluetoothAddress &remoteDevice, QObject *parent = nullptr); // TODO Qt 6 remove ctor explicit QLowEnergyController(const QBluetoothDeviceInfo &remoteDevice, - QObject *parent = nullptr); + QObject *parent = nullptr); // TODO Qt 6 make private explicit QLowEnergyController(const QBluetoothAddress &remoteDevice, const QBluetoothAddress &localDevice, QObject *parent = nullptr); // TODO Qt 6 remove ctor static QLowEnergyController *createCentral(const QBluetoothDeviceInfo &remoteDevice, QObject *parent = nullptr); + static QLowEnergyController *createCentral(const QBluetoothAddress &remoteDevice, + const QBluetoothAddress &localDevice, + QObject *parent = nullptr); static QLowEnergyController *createPeripheral(QObject *parent = nullptr); // TODO: Allow to set connection timeout (disconnect when no data has been exchanged for n seconds). diff --git a/src/bluetooth/qlowenergycontroller_bluezdbus.cpp b/src/bluetooth/qlowenergycontroller_bluezdbus.cpp index 441eca6b..88ae0320 100644 --- a/src/bluetooth/qlowenergycontroller_bluezdbus.cpp +++ b/src/bluetooth/qlowenergycontroller_bluezdbus.cpp @@ -268,9 +268,13 @@ void QLowEnergyControllerPrivateBluezDBus::connectToDeviceHelper() const QVariantMap &ifaceValues = jt.value(); if (iface == QStringLiteral("org.bluez.Device1")) { - if (remoteDevice.toString() == ifaceValues.value(QStringLiteral("Address")).toString()) { - devicePath = it.key().path(); - break; + if (remoteDevice.toString() == ifaceValues.value(QStringLiteral("Address")).toString()) + { + const QVariant adapterForCurrentDevice = ifaceValues.value(QStringLiteral("Adapter")); + if (qvariant_cast(adapterForCurrentDevice).path() == hostAdapterPath) { + devicePath = it.key().path(); + break; + } } } } -- cgit v1.2.3 From ca0594270cd2905c904c3a7ac578f5e1ac71f962 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 11 Feb 2019 11:54:26 +0100 Subject: btscanner example: Fix deprecation warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit device.cpp:112:49: warning: ‘void QListWidgetItem::setTextColor(const QColor&)’ is deprecated: Use QListWidgetItem::setForeground() instead [-Wdeprecated-declarations] device.cpp:114:49: warning: ‘void QListWidgetItem::setTextColor(const QColor&)’ is deprecated: Use QListWidgetItem::setForeground() instead [-Wdeprecated-declarations] device.cpp:221:49: warning: ‘void QListWidgetItem::setTextColor(const QColor&)’ is deprecated: Use QListWidgetItem::setForeground() instead [-Wdeprecated-declarations] device.cpp:226:47: warning: ‘void QListWidgetItem::setTextColor(const QColor&)’ is deprecated: Use QListWidgetItem::setForeground() instead [-Wdeprecated-declarations] Change-Id: I673b786d2d7b76ca4a51ba8eaad231592f4ad4fd Reviewed-by: Alex Blasche --- examples/bluetooth/btscanner/device.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/bluetooth/btscanner/device.cpp b/examples/bluetooth/btscanner/device.cpp index e97c0637..b6a07db4 100644 --- a/examples/bluetooth/btscanner/device.cpp +++ b/examples/bluetooth/btscanner/device.cpp @@ -109,9 +109,9 @@ void DeviceDiscoveryDialog::addDevice(const QBluetoothDeviceInfo &info) QListWidgetItem *item = new QListWidgetItem(label); QBluetoothLocalDevice::Pairing pairingStatus = localDevice->pairingStatus(info.address()); if (pairingStatus == QBluetoothLocalDevice::Paired || pairingStatus == QBluetoothLocalDevice::AuthorizedPaired ) - item->setTextColor(QColor(Qt::green)); + item->setForeground(QColor(Qt::green)); else - item->setTextColor(QColor(Qt::black)); + item->setForeground(QColor(Qt::black)); ui->list->addItem(item); } @@ -218,12 +218,12 @@ void DeviceDiscoveryDialog::pairingDone(const QBluetoothAddress &address, QBluet if (pairing == QBluetoothLocalDevice::Paired || pairing == QBluetoothLocalDevice::AuthorizedPaired ) { for (int var = 0; var < items.count(); ++var) { QListWidgetItem *item = items.at(var); - item->setTextColor(QColor(Qt::green)); + item->setForeground(QColor(Qt::green)); } } else { for (int var = 0; var < items.count(); ++var) { QListWidgetItem *item = items.at(var); - item->setTextColor(QColor(Qt::red)); + item->setForeground(QColor(Qt::red)); } } } -- cgit v1.2.3 From a3ffef76acdd4ea4b81bde481948ed4b953289af Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 11 Feb 2019 11:45:40 +0100 Subject: QDeclarativeBluetoothDiscoveryModel: Fix warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement QAbstractItemModel::roleNames(), fixing the deprecationw arning: qdeclarativebluetoothdiscoverymodel.cpp:175:27: warning: ‘void QAbstractItemModel::setRoleNames(const QHash&)’ is deprecated [-Wdeprecated-declarations] Add missing overrides to silence new warnings about insconsistent override. Fixes: QTBUG-73717 Change-Id: Iea9bd1d6cccfbecac7e3cf56101a5d65d466c023 Reviewed-by: Alex Blasche --- .../bluetooth/qdeclarativebluetoothdiscoverymodel.cpp | 16 ++++++++-------- .../bluetooth/qdeclarativebluetoothdiscoverymodel_p.h | 10 ++++++---- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/imports/bluetooth/qdeclarativebluetoothdiscoverymodel.cpp b/src/imports/bluetooth/qdeclarativebluetoothdiscoverymodel.cpp index 6b2f32f3..6213355e 100644 --- a/src/imports/bluetooth/qdeclarativebluetoothdiscoverymodel.cpp +++ b/src/imports/bluetooth/qdeclarativebluetoothdiscoverymodel.cpp @@ -165,14 +165,6 @@ QDeclarativeBluetoothDiscoveryModel::QDeclarativeBluetoothDiscoveryModel(QObject this, &QDeclarativeBluetoothDiscoveryModel::errorDiscovery); d->m_serviceAgent->setObjectName(QStringLiteral("ServiceDiscoveryAgent")); - - QHash roleNames; - roleNames = QAbstractItemModel::roleNames(); - roleNames.insert(Name, "name"); - roleNames.insert(ServiceRole, "service"); - roleNames.insert(RemoteAddress, "remoteAddress"); - roleNames.insert(DeviceName, "deviceName"); - setRoleNames(roleNames); } QDeclarativeBluetoothDiscoveryModel::~QDeclarativeBluetoothDiscoveryModel() @@ -314,6 +306,14 @@ QVariant QDeclarativeBluetoothDiscoveryModel::data(const QModelIndex &index, int return QVariant(); } +QHash QDeclarativeBluetoothDiscoveryModel::roleNames() const +{ + return {{Name, "name"}, + {ServiceRole, "service"}, + {RemoteAddress, "remoteAddress"}, + {DeviceName, "deviceName"}}; +} + /*! \qmlsignal BluetoothDiscoveryModel::serviceDiscovered(BluetoothService service) diff --git a/src/imports/bluetooth/qdeclarativebluetoothdiscoverymodel_p.h b/src/imports/bluetooth/qdeclarativebluetoothdiscoverymodel_p.h index 0aa134f5..6cbde088 100644 --- a/src/imports/bluetooth/qdeclarativebluetoothdiscoverymodel_p.h +++ b/src/imports/bluetooth/qdeclarativebluetoothdiscoverymodel_p.h @@ -106,13 +106,15 @@ public: Error error() const; - void componentComplete(); + void componentComplete() override; - void classBegin() { } + void classBegin() override { } // From QAbstractListModel - int rowCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; + int rowCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + + QHash roleNames() const override; DiscoveryMode discoveryMode() const; void setDiscoveryMode(DiscoveryMode discovery); -- cgit v1.2.3 From 68982ef782d7e7c23ce25619f8be2bee1bca355d Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Mon, 11 Feb 2019 16:28:05 +0100 Subject: Ensure SDP records can be byte arrays/hex encoded This addresses the issue on Bluez only. macOS ignore such attribute values and WinRT implicitly converts them to hex strings. The macOS debug stream operator produced slightly different output compared to the other platforms. The output between the platforms must match though. Therefore, the general version was copied over to macOS. Task-number: QTBUG-73328 Change-Id: Ieea2a3a559b5686f7f7d16d5c75dd9ef2782cdf5 Reviewed-by: Oliver Wolff --- src/bluetooth/qbluetoothserviceinfo.cpp | 9 +++- src/bluetooth/qbluetoothserviceinfo_bluez.cpp | 17 +++---- src/bluetooth/qbluetoothserviceinfo_osx.mm | 59 +++++++++++++--------- src/bluetooth/qbluetoothserviceinfo_winrt.cpp | 8 +++ .../tst_qbluetoothserviceinfo.cpp | 45 +++++++++++++++++ 5 files changed, 105 insertions(+), 33 deletions(-) diff --git a/src/bluetooth/qbluetoothserviceinfo.cpp b/src/bluetooth/qbluetoothserviceinfo.cpp index 74b17ac4..7c3780ec 100644 --- a/src/bluetooth/qbluetoothserviceinfo.cpp +++ b/src/bluetooth/qbluetoothserviceinfo.cpp @@ -413,6 +413,9 @@ void QBluetoothServiceInfo::setDevice(const QBluetoothDeviceInfo &device) If the service information is already registered with the platform's SDP database, the database entry will not be updated until \l registerService() was called again. + \note If an attribute expectes a byte-encoded value (e.g. Bluetooth HID services), + it should be set as QByteArray. + \sa isRegistered(), registerService() */ void QBluetoothServiceInfo::setAttribute(quint16 attributeId, const QVariant &value) @@ -578,6 +581,10 @@ static void dumpAttributeVariant(QDebug dbg, const QVariant &var, const QString& dbg << QString::asprintf("%sstring %s\n", indent.toUtf8().constData(), var.toString().toUtf8().constData()); break; + case QMetaType::QByteArray: + dbg << QString::asprintf("%sbytearray %s\n", indent.toUtf8().constData(), + var.toByteArray().toHex().constData()); + break; case QMetaType::Bool: dbg << QString::asprintf("%sbool %d\n", indent.toUtf8().constData(), var.toBool()); break; @@ -631,7 +638,7 @@ QDebug operator<<(QDebug dbg, const QBluetoothServiceInfo &info) { QDebugStateSaver saver(dbg); dbg.noquote() << "\n"; - QList attributes = info.attributes(); + const QList attributes = info.attributes(); for (quint16 id : attributes) { dumpAttributeVariant(dbg, info.attribute(id), QStringLiteral("(%1)\t").arg(id)); } diff --git a/src/bluetooth/qbluetoothserviceinfo_bluez.cpp b/src/bluetooth/qbluetoothserviceinfo_bluez.cpp index 09829b13..5f57e19e 100644 --- a/src/bluetooth/qbluetoothserviceinfo_bluez.cpp +++ b/src/bluetooth/qbluetoothserviceinfo_bluez.cpp @@ -103,17 +103,16 @@ static void writeAttribute(QXmlStreamWriter *stream, const QVariant &attribute) QString::number(attribute.value(), 16)); //stream->writeAttribute(QStringLiteral("name"), foo); break; + case QMetaType::QByteArray: + stream->writeEmptyElement(QStringLiteral("text")); + stream->writeAttribute(QStringLiteral("value"), + QString::fromLatin1(attribute.value().toHex().constData())); + stream->writeAttribute(QStringLiteral("encoding"), QStringLiteral("hex")); + break; case QMetaType::QString: stream->writeEmptyElement(QStringLiteral("text")); - if (/* require hex encoding */ false) { - stream->writeAttribute(QStringLiteral("value"), QString::fromLatin1( - attribute.value().toUtf8().toHex().constData())); - stream->writeAttribute(QStringLiteral("encoding"), QStringLiteral("hex")); - } else { - stream->writeAttribute(QStringLiteral("value"), attribute.value()); - stream->writeAttribute(QStringLiteral("encoding"), QStringLiteral("normal")); - } - //stream->writeAttribute(QStringLiteral("name"), foo); + stream->writeAttribute(QStringLiteral("value"), attribute.value()); + stream->writeAttribute(QStringLiteral("encoding"), QStringLiteral("normal")); break; case QMetaType::Bool: stream->writeEmptyElement(QStringLiteral("boolean")); diff --git a/src/bluetooth/qbluetoothserviceinfo_osx.mm b/src/bluetooth/qbluetoothserviceinfo_osx.mm index 27da70fc..34de4695 100644 --- a/src/bluetooth/qbluetoothserviceinfo_osx.mm +++ b/src/bluetooth/qbluetoothserviceinfo_osx.mm @@ -304,80 +304,93 @@ QBluetoothServiceInfo &QBluetoothServiceInfo::operator=(const QBluetoothServiceI return *this; } -static void dumpAttributeVariant(const QVariant &var, const QString indent) +static void dumpAttributeVariant(QDebug dbg, const QVariant &var, const QString& indent) { switch (int(var.type())) { case QMetaType::Void: - qDebug("%sEmpty", indent.toLocal8Bit().constData()); + dbg << QString::asprintf("%sEmpty\n", indent.toUtf8().constData()); break; case QMetaType::UChar: - qDebug("%suchar %u", indent.toLocal8Bit().constData(), var.toUInt()); + dbg << QString::asprintf("%suchar %u\n", indent.toUtf8().constData(), var.toUInt()); break; case QMetaType::UShort: - qDebug("%sushort %u", indent.toLocal8Bit().constData(), var.toUInt()); + dbg << QString::asprintf("%sushort %u\n", indent.toUtf8().constData(), var.toUInt()); + break; case QMetaType::UInt: - qDebug("%suint %u", indent.toLocal8Bit().constData(), var.toUInt()); + dbg << QString::asprintf("%suint %u\n", indent.toUtf8().constData(), var.toUInt()); break; case QMetaType::Char: - qDebug("%schar %d", indent.toLocal8Bit().constData(), var.toInt()); + dbg << QString::asprintf("%schar %d\n", indent.toUtf8().constData(), var.toInt()); break; case QMetaType::Short: - qDebug("%sshort %d", indent.toLocal8Bit().constData(), var.toInt()); + dbg << QString::asprintf("%sshort %d\n", indent.toUtf8().constData(), var.toInt()); break; case QMetaType::Int: - qDebug("%sint %d", indent.toLocal8Bit().constData(), var.toInt()); + dbg << QString::asprintf("%sint %d\n", indent.toUtf8().constData(), var.toInt()); break; case QMetaType::QString: - qDebug("%sstring %s", indent.toLocal8Bit().constData(), var.toString().toLocal8Bit().constData()); + dbg << QString::asprintf("%sstring %s\n", indent.toUtf8().constData(), + var.toString().toUtf8().constData()); + break; + case QMetaType::QByteArray: + dbg << QString::asprintf("%sbytearray %s\n", indent.toUtf8().constData(), + var.toByteArray().toHex().constData()); break; case QMetaType::Bool: - qDebug("%sbool %d", indent.toLocal8Bit().constData(), var.toBool()); + dbg << QString::asprintf("%sbool %d\n", indent.toUtf8().constData(), var.toBool()); break; case QMetaType::QUrl: - qDebug("%surl %s", indent.toLocal8Bit().constData(), var.toUrl().toString().toLocal8Bit().constData()); + dbg << QString::asprintf("%surl %s\n", indent.toUtf8().constData(), + var.toUrl().toString().toUtf8().constData()); break; case QVariant::UserType: if (var.userType() == qMetaTypeId()) { QBluetoothUuid uuid = var.value(); switch (uuid.minimumSize()) { case 0: - qDebug("%suuid NULL", indent.toLocal8Bit().constData()); + dbg << QString::asprintf("%suuid NULL\n", indent.toUtf8().constData()); break; case 2: - qDebug("%suuid %04x", indent.toLocal8Bit().constData(), uuid.toUInt16()); + dbg << QString::asprintf("%suuid2 %04x\n", indent.toUtf8().constData(), + uuid.toUInt16()); break; case 4: - qDebug("%suuid %08x", indent.toLocal8Bit().constData(), uuid.toUInt32()); + dbg << QString::asprintf("%suuid %08x\n", indent.toUtf8().constData(), + uuid.toUInt32()); break; case 16: - qDebug("%suuid %s", indent.toLocal8Bit().constData(), QByteArray(reinterpret_cast(uuid.toUInt128().data), 16).toHex().constData()); + dbg << QString::asprintf("%suuid %s\n", + indent.toUtf8().constData(), + QByteArray(reinterpret_cast(uuid.toUInt128().data), 16).toHex().constData()); break; default: - qDebug("%suuid ???", indent.toLocal8Bit().constData()); - ; + dbg << QString::asprintf("%suuid ???\n", indent.toUtf8().constData()); } } else if (var.userType() == qMetaTypeId()) { - qDebug("%sSequence", indent.toLocal8Bit().constData()); + dbg << QString::asprintf("%sSequence\n", indent.toUtf8().constData()); const QBluetoothServiceInfo::Sequence *sequence = static_cast(var.data()); for (const QVariant &v : *sequence) - dumpAttributeVariant(v, indent + QLatin1Char('\t')); + dumpAttributeVariant(dbg, v, indent + QLatin1Char('\t')); } else if (var.userType() == qMetaTypeId()) { - qDebug("%sAlternative", indent.toLocal8Bit().constData()); + dbg << QString::asprintf("%sAlternative\n", indent.toUtf8().constData()); const QBluetoothServiceInfo::Alternative *alternative = static_cast(var.data()); for (const QVariant &v : *alternative) - dumpAttributeVariant(v, indent + QLatin1Char('\t')); + dumpAttributeVariant(dbg, v, indent + QLatin1Char('\t')); } break; default: - qDebug("%sunknown variant type %d", indent.toLocal8Bit().constData(), var.userType()); + dbg << QString::asprintf("%sunknown variant type %d\n", indent.toUtf8().constData(), + var.userType()); } } QDebug operator << (QDebug dbg, const QBluetoothServiceInfo &info) { + QDebugStateSaver saver(dbg); + dbg.noquote() << "\n"; const QList attributes = info.attributes(); for (quint16 id : attributes) { - dumpAttributeVariant(info.attribute(id), QString::fromLatin1("(%1)\t").arg(id)); + dumpAttributeVariant(dbg, info.attribute(id), QString::fromLatin1("(%1)\t").arg(id)); } return dbg; } diff --git a/src/bluetooth/qbluetoothserviceinfo_winrt.cpp b/src/bluetooth/qbluetoothserviceinfo_winrt.cpp index 45262735..e806096f 100644 --- a/src/bluetooth/qbluetoothserviceinfo_winrt.cpp +++ b/src/bluetooth/qbluetoothserviceinfo_winrt.cpp @@ -297,6 +297,14 @@ static ComPtr bufferFromAttribute(const QVariant &attribute) hr = writer->WriteInt64(attribute.value()); Q_ASSERT_SUCCEEDED(hr); break; + case QMetaType::QByteArray: { + qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering attribute of type QMetaType::QByteArray:" << attribute.value(); + const QString stringValue = QString::fromLatin1(attribute.value().toHex()); + const bool writeSuccess = writeStringHelper(stringValue, writer); + if (!writeSuccess) + return nullptr; + break; + } case QMetaType::QString: { qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering attribute of type QMetaType::QString:" << attribute.value(); const QString stringValue = attribute.value(); diff --git a/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp b/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp index ae8cf5d0..10c4bd3b 100644 --- a/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp +++ b/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp @@ -61,6 +61,8 @@ private slots: void tst_assignment(); void tst_serviceClassUuids(); + + void tst_writeByteArray(); }; tst_QBluetoothServiceInfo::tst_QBluetoothServiceInfo() @@ -385,6 +387,49 @@ void tst_QBluetoothServiceInfo::tst_serviceClassUuids() QCOMPARE(svclids.at(1), QBluetoothUuid(QBluetoothUuid::SerialPort)); } +static QByteArray debugOutput; + +void debugHandler(QtMsgType type, const QMessageLogContext &, const QString &msg) +{ + switch (type) { + case QtDebugMsg : + debugOutput = msg.toLocal8Bit(); + break; + default: + break; + } +} + +void tst_QBluetoothServiceInfo::tst_writeByteArray() +{ + // We cannot directly test the produced XML output for Bluez + // as there no public API to retrieve it and it would be Bluez specific. + // However we can check the debug output. + // It should contain a qbyteArray rather than a string. In the XML the QByteArray + // is converted to a text tag with hex encoding. + + const QByteArray expected("\n (518)\tSequence\n (518)\t\tSequence\n (518)\t\t\tuchar 34\n (518)\t\t\tbytearray 05010906a101850105079508750119e029e7150025018102950175088103050795067508150026ff00190029ff8100050895057501190129059102950175039103c005010902a10185020901a1000509190129031500250175019503810275059501810105010930093109381581257f750895038106c0c0\n"); + + const QByteArray hidDescriptor = + QByteArray::fromHex("05010906a101850105079508750119e029e7150025018102950175088103050795067508150026FF00190029FF8100050895057501190129059102950175039103c005010902a10185020901a1000509190129031500250175019503810275059501810105010930093109381581257f750895038106c0c0"); + const QBluetoothServiceInfo::Sequence hidDescriptorList({ + QVariant::fromValue(quint8(0x22)), // Report type + QByteArray(hidDescriptor) // Descriptor array + }); + const QBluetoothServiceInfo::Sequence hidDescriptorListSeq({ + QVariant::fromValue(hidDescriptorList) + }); + QBluetoothServiceInfo srvInfo; + srvInfo.setAttribute(0x0206, QVariant::fromValue(hidDescriptorListSeq)); + + const QVariant attribute = srvInfo.attribute(0x0206); + debugOutput.clear(); + qInstallMessageHandler(debugHandler); + qDebug() << srvInfo; + qInstallMessageHandler(nullptr); + QCOMPARE(debugOutput, expected); +} + QTEST_MAIN(tst_QBluetoothServiceInfo) #include "tst_qbluetoothserviceinfo.moc" -- cgit v1.2.3 From f5313bc82882cd5f211726f185dc77d106f41a49 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Mon, 11 Feb 2019 16:34:13 +0100 Subject: Cleanup QBluetoothServiceInfo xml stream writer on Bluez Change-Id: Iad66628307a2167cdd6c64080457c670697bd1f7 Reviewed-by: Timur Pocheptsov --- src/bluetooth/qbluetoothserviceinfo_bluez.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/bluetooth/qbluetoothserviceinfo_bluez.cpp b/src/bluetooth/qbluetoothserviceinfo_bluez.cpp index 5f57e19e..f719779a 100644 --- a/src/bluetooth/qbluetoothserviceinfo_bluez.cpp +++ b/src/bluetooth/qbluetoothserviceinfo_bluez.cpp @@ -69,39 +69,33 @@ static void writeAttribute(QXmlStreamWriter *stream, const QVariant &attribute) stream->writeAttribute(QStringLiteral("value"), unsignedFormat.arg(attribute.value(), 2, 16, QLatin1Char('0'))); - //stream->writeAttribute(QStringLiteral("name"), foo); break; case QMetaType::UShort: stream->writeEmptyElement(QStringLiteral("uint16")); stream->writeAttribute(QStringLiteral("value"), unsignedFormat.arg(attribute.value(), 4, 16, QLatin1Char('0'))); - //stream->writeAttribute(QStringLiteral("name"), foo); break; case QMetaType::UInt: stream->writeEmptyElement(QStringLiteral("uint32")); stream->writeAttribute(QStringLiteral("value"), unsignedFormat.arg(attribute.value(), 8, 16, QLatin1Char('0'))); - //stream->writeAttribute(QStringLiteral("name"), foo); break; case QMetaType::Char: stream->writeEmptyElement(QStringLiteral("int8")); stream->writeAttribute(QStringLiteral("value"), QString::number(attribute.value(), 16)); - //stream->writeAttribute(QStringLiteral("name"), foo); break; case QMetaType::Short: stream->writeEmptyElement(QStringLiteral("int16")); stream->writeAttribute(QStringLiteral("value"), QString::number(attribute.value(), 16)); - //stream->writeAttribute(QStringLiteral("name"), foo); break; case QMetaType::Int: stream->writeEmptyElement(QStringLiteral("int32")); stream->writeAttribute(QStringLiteral("value"), QString::number(attribute.value(), 16)); - //stream->writeAttribute(QStringLiteral("name"), foo); break; case QMetaType::QByteArray: stream->writeEmptyElement(QStringLiteral("text")); @@ -114,20 +108,18 @@ static void writeAttribute(QXmlStreamWriter *stream, const QVariant &attribute) stream->writeAttribute(QStringLiteral("value"), attribute.value()); stream->writeAttribute(QStringLiteral("encoding"), QStringLiteral("normal")); break; - case QMetaType::Bool: + case QMetaType::Bool: stream->writeEmptyElement(QStringLiteral("boolean")); if (attribute.value()) stream->writeAttribute(QStringLiteral("value"), QStringLiteral("true")); else stream->writeAttribute(QStringLiteral("value"), QStringLiteral("false")); - //stream->writeAttribute(QStringLiteral("name"), foo); break; - case QMetaType::QUrl: + case QMetaType::QUrl: stream->writeEmptyElement(QStringLiteral("url")); stream->writeAttribute(QStringLiteral("value"), attribute.value().toString()); - //stream->writeAttribute(QStringLiteral("name"), foo); break; - case QVariant::UserType: + case QVariant::UserType: if (attribute.userType() == qMetaTypeId()) { stream->writeEmptyElement(QStringLiteral("uuid")); -- cgit v1.2.3 From 1b19d7981610c2a9587544da654b4ca783e1bf6b Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Tue, 12 Feb 2019 09:12:00 +0100 Subject: Ensure signed int are written in base 10 notation The code was converting signed ints/chars/shorts to hex notation without prepanding the '0x'. This led to Bluez reading the value as base 10 value and created the problem that values containing letters were improperly parsed. Fixes: QTBUG-73330 Change-Id: I9407a82495761b2e541e13b282ad6ccdb7cdd84a Reviewed-by: Oliver Wolff --- src/bluetooth/qbluetoothserviceinfo_bluez.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bluetooth/qbluetoothserviceinfo_bluez.cpp b/src/bluetooth/qbluetoothserviceinfo_bluez.cpp index f719779a..d91367c4 100644 --- a/src/bluetooth/qbluetoothserviceinfo_bluez.cpp +++ b/src/bluetooth/qbluetoothserviceinfo_bluez.cpp @@ -85,17 +85,17 @@ static void writeAttribute(QXmlStreamWriter *stream, const QVariant &attribute) case QMetaType::Char: stream->writeEmptyElement(QStringLiteral("int8")); stream->writeAttribute(QStringLiteral("value"), - QString::number(attribute.value(), 16)); + QString::number(attribute.value())); break; case QMetaType::Short: stream->writeEmptyElement(QStringLiteral("int16")); stream->writeAttribute(QStringLiteral("value"), - QString::number(attribute.value(), 16)); + QString::number(attribute.value())); break; case QMetaType::Int: stream->writeEmptyElement(QStringLiteral("int32")); stream->writeAttribute(QStringLiteral("value"), - QString::number(attribute.value(), 16)); + QString::number(attribute.value())); break; case QMetaType::QByteArray: stream->writeEmptyElement(QStringLiteral("text")); -- cgit v1.2.3 From b58c470962aa6831f53d088afd523c1b1530fbc8 Mon Sep 17 00:00:00 2001 From: Kari Oikarinen Date: Mon, 25 Feb 2019 09:37:51 +0200 Subject: Bump version Change-Id: Ie9931fd14cbc470484da65919da30ec97b845eec --- .qmake.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.qmake.conf b/.qmake.conf index f8cda0e7..db2e0b65 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,3 +1,3 @@ load(qt_build_config) -MODULE_VERSION = 5.13.0 +MODULE_VERSION = 5.14.0 -- cgit v1.2.3 From eec8fed760a572eb9187f79b43d87986f2b4f404 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Wed, 20 Feb 2019 15:21:30 +0200 Subject: Add changes file for Qt 5.12.2 + 45608abda7b9470ec0b3ea26e849ec71b637288d Don't reverse uuids which are derived from Bluetooth's base uuid + e957470ae0e61eea1afa96d76468dbdc87168421 Ensure custom uuids are returned by QBluetoothServiceInfo::serviceUuid() + 41ac7f7da4367d4923b19c693f469425b8e6e0f0 Fix a few clazy warnings + 0b4aadadf19adff87fd6a847458be742220fc8fd Bump version + 1bba2694302b7200d62d715827f20e8f830d9e03 Check for system feature PackageManager.FEATURE_NFC + c6cd43df27eb967404c10c1216c0f186fc03b226 LE/Android: trivial typo fixes + 1d964be81b0081d2ccdbf5c9a875672f447b5977 LE/Android: fix crash when destroying DiscoveryAgent during scan + 80a1346a139343b221d49b7c7b7fabcd55669a65 Doc: Fix link error to \macos + 7accd34495a8269f9b335446ae779bd56cd4c4c0 Bluetooth LE scan - fix a crash (CoreBluetooth) + d41eaba6fb19350d51e6487440af13ac4320f46e Fix potential bug in osxbtcentralmanager + 862c766f0896314bf850c5573bb39788f211c1e1 Add Android exclamation to QBluetoothServiceDiscoveryAgent ctor docs + 1606ccb76ba72990df652fbd7f01d709ae20b63c Fix unit test failure on Android + 68982ef782d7e7c23ce25619f8be2bee1bca355d Ensure SDP records can be byte arrays/hex encoded + f5313bc82882cd5f211726f185dc77d106f41a49 Cleanup QBluetoothServiceInfo xml stream writer on Bluez + 1b19d7981610c2a9587544da654b4ca783e1bf6b Ensure signed int are written in base 10 notation Change-Id: I7ea4e13ad4b4612ab5688c08912c1f321e0b9c9d Reviewed-by: Timur Pocheptsov --- dist/changes-5.12.2 | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 dist/changes-5.12.2 diff --git a/dist/changes-5.12.2 b/dist/changes-5.12.2 new file mode 100644 index 00000000..9e0f7fa0 --- /dev/null +++ b/dist/changes-5.12.2 @@ -0,0 +1,61 @@ +Qt 5.12.2 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.12.0 through 5.12.1. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +https://doc.qt.io/qt-5/index.html + +The Qt version 5.12 series is binary compatible with the 5.11.x series. +Applications compiled for 5.11 will continue to run with 5.12. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +*************************************************************************** +* Qt 5.12.2 Changes * +*************************************************************************** + +QtBluetooth +----------- + + - Added documentation improvements to QBluetoothServiceDiscoveryAgent. + +QtNfc +----- + + - Fixed exceptions on Android WearOS devices because those devices not + supporting NFC. + +*************************************************************************** +* Platform Specific Changes * +*************************************************************************** + +Android +------- + + - [QTBUG-72681] Fixed wrong reversing of service uuids when calling + QBLuetoothSocket::connectToService(). + + - Fixed a crash when destroying QBluetoothDeviceDisocveryAgent during an + ongoing scan. + + - [QTBUG-73571] Fixed tst_QBluetoothServiceDiscoveryAgent::tst_invalidBtAddress() + which was failing on Android. + +iOS/macOS +--------- + + - [QTBUG-73140] Fixed a crash in QBluetoothDeviceDiscoveryAgent when the user + turns off Bluetooth while a scan is running. + +Linux/BlueZ +----------- + + - [QTBUG-72800] Addressed bug where a discovered custom uuid was not returned + via BluetoothServiceInfo::serviceUuid() but as serviceClassUuids(). -- cgit v1.2.3 From 3f9ba9c07322d90f8f918d9e144e84d422e4cfbd Mon Sep 17 00:00:00 2001 From: Christian Wassmuth Date: Tue, 5 Mar 2019 12:53:19 +0100 Subject: Bluez-DBus: Fix crash at calling disconnect while not connected Calling disconnectFromDevice() while not connected or in ClosingState caused a crash on accessing device which is null. [ChangeLog][QtBluetooth][Bluez-DBus] Fix crash at calling QLowEnergyController::disconnectFromDevice while not connected Change-Id: I5979e3e5fca62c4a1469014e4f553c7db24f8dac Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_bluezdbus.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bluetooth/qlowenergycontroller_bluezdbus.cpp b/src/bluetooth/qlowenergycontroller_bluezdbus.cpp index 441eca6b..f019d9c2 100644 --- a/src/bluetooth/qlowenergycontroller_bluezdbus.cpp +++ b/src/bluetooth/qlowenergycontroller_bluezdbus.cpp @@ -350,6 +350,9 @@ void QLowEnergyControllerPrivateBluezDBus::connectToDevice() void QLowEnergyControllerPrivateBluezDBus::disconnectFromDevice() { + if (!device) + return; + setState(QLowEnergyController::ClosingState); QDBusPendingReply<> reply = device->Disconnect(); -- cgit v1.2.3 From 09c476732bd79dcfa03bd8273fbd0eafb4b95a53 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Mon, 18 Mar 2019 13:30:18 +0100 Subject: Fix warning about initialized but not referenced variable Change-Id: Iabb236577d813bf72b2237fedc60b894fae06bc4 Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp index a353f5e3..18ea9495 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp @@ -250,7 +250,8 @@ void QWinRTBluetoothDeviceDiscoveryWorker::gatherMultipleDeviceInformation(quint { for (quint32 i = 0; i < deviceCount; ++i) { ComPtr device; - HRESULT hr = devices->GetAt(i, &device); + HRESULT hr; + hr = devices->GetAt(i, &device); Q_ASSERT_SUCCEEDED(hr); gatherDeviceInformation(device.Get(), mode); } -- cgit v1.2.3 From ecc177dba5833ebf259d8af7af00a5e86f451fdc Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Thu, 7 Mar 2019 12:57:48 +0100 Subject: winrt: Implement rssi Task-number: QTBUG-71943 Change-Id: I5f508c6d858f088d518fc0b3ad9644273f31e2de Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothdevicediscoveryagent_p.h | 1 + .../qbluetoothdevicediscoveryagent_winrt.cpp | 77 ++++++++++++++++++---- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index ce31392f..e4370180 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -167,6 +167,7 @@ private: #ifdef QT_WINRT_BLUETOOTH private slots: void registerDevice(const QBluetoothDeviceInfo &info); + void updateDeviceRssi(const QBluetoothAddress &address, qint16 rssi); void onScanFinished(); private: diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp index 18ea9495..61f167de 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp @@ -117,6 +117,7 @@ public slots: Q_SIGNALS: void deviceFound(const QBluetoothDeviceInfo &info); + void deviceRssiChanged(const QBluetoothAddress &address, qint16 rssi); void scanFinished(); public: @@ -127,9 +128,14 @@ private: EventRegistrationToken m_leDeviceAddedToken; #if QT_CONFIG(winrt_btle_no_pairing) QMutex m_foundDevicesMutex; - QMap> m_foundLEDevicesMap; + struct LEAdvertisingInfo { + QVector services; + qint16 rssi = 0; + }; + + QMap m_foundLEDevicesMap; #endif - QVector m_foundLEDevices; + QMap m_foundLEDevices; int m_pendingPairedDevices; ComPtr m_deviceStatics; @@ -272,6 +278,9 @@ void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher() HRESULT hr; hr = args->get_BluetoothAddress(&address); Q_ASSERT_SUCCEEDED(hr); + qint16 rssi; + hr = args->get_RawSignalStrengthInDBm(&rssi); + Q_ASSERT_SUCCEEDED(hr); #if QT_CONFIG(winrt_btle_no_pairing) if (supportsNewLEApi()) { ComPtr ad; @@ -296,7 +305,13 @@ void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher() if (m_foundLEDevicesMap.contains(address)) { if (size == 0) return S_OK; - QVector foundServices = m_foundLEDevicesMap.value(address); + const LEAdvertisingInfo adInfo = m_foundLEDevicesMap.value(address); + QVector foundServices = adInfo.services; + bool rssiChanged = false; + if (adInfo.rssi != rssi) { + m_foundLEDevicesMap[address].rssi = rssi; + rssiChanged = true; + } bool newServiceAdded = false; for (const QBluetoothUuid &uuid : qAsConst(serviceUuids)) { if (!foundServices.contains(uuid)) { @@ -304,20 +319,36 @@ void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher() newServiceAdded = true; } } - if (!newServiceAdded) + if (!newServiceAdded) { + if (rssiChanged) { + QMetaObject::invokeMethod(this, "deviceRssiChanged", Qt::AutoConnection, + Q_ARG(QBluetoothAddress, QBluetoothAddress(address)), + Q_ARG(qint16, rssi)); + } return S_OK; - m_foundLEDevicesMap[address] = foundServices; + } + m_foundLEDevicesMap[address].services = foundServices; } else { - m_foundLEDevicesMap.insert(address, serviceUuids); + LEAdvertisingInfo info; + info.services = std::move(serviceUuids); + info.rssi = rssi; + m_foundLEDevicesMap.insert(address, info); } locker.unlock(); } else #endif { - if (m_foundLEDevices.contains(address)) + if (m_foundLEDevices.contains(address)) { + if (m_foundLEDevices.value(address) != rssi) { + m_foundLEDevices[address] = rssi; + QMetaObject::invokeMethod(this, "deviceRssiChanged", Qt::AutoConnection, + Q_ARG(QBluetoothAddress, QBluetoothAddress(address)), + Q_ARG(qint16, rssi)); + } return S_OK; - m_foundLEDevices.append(address); + } + m_foundLEDevices.insert(address, rssi); } leBluetoothInfoFromAddressAsync(address); return S_OK; @@ -617,13 +648,15 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr uuids; + const LEAdvertisingInfo adInfo = m_foundLEDevicesMap.value(address); + const qint16 rssi = adInfo.rssi; // Use the services obtained from the advertisement data if the device is not paired if (!isPaired) { - uuids = m_foundLEDevicesMap.value(address).toList(); + uuids = adInfo.services.toList(); } else { IVectorView *deviceServices; hr = device->get_GattServices(&deviceServices); @@ -690,11 +725,12 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtrstart(); @@ -794,6 +832,21 @@ void QBluetoothDeviceDiscoveryAgentPrivate::registerDevice(const QBluetoothDevic emit q->deviceDiscovered(info); } +void QBluetoothDeviceDiscoveryAgentPrivate::updateDeviceRssi(const QBluetoothAddress &address, qint16 rssi) +{ + Q_Q(QBluetoothDeviceDiscoveryAgent); + for (QList::iterator iter = discoveredDevices.begin(); + iter != discoveredDevices.end(); ++iter) { + if (iter->address() == address) { + qCDebug(QT_BT_WINRT) << "Updating rssi for device" << iter->name() << iter->address() + << "from" << iter->rssi() << "to" << rssi; + iter->setRssi(rssi); + emit q->deviceUpdated(*iter, QBluetoothDeviceInfo::Field::RSSI); + return; + } + } +} + void QBluetoothDeviceDiscoveryAgentPrivate::onScanFinished() { Q_Q(QBluetoothDeviceDiscoveryAgent); @@ -811,6 +864,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::disconnectAndClearWorker() this, &QBluetoothDeviceDiscoveryAgentPrivate::onScanFinished); disconnect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::deviceFound, q, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered); + disconnect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::deviceRssiChanged, + this, &QBluetoothDeviceDiscoveryAgentPrivate::updateDeviceRssi); if (leScanTimer) { disconnect(leScanTimer, &QTimer::timeout, worker, &QWinRTBluetoothDeviceDiscoveryWorker::finishDiscovery); -- cgit v1.2.3 From 2fd1ed79a65f2c9d1469795683f36e40c3bfa897 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Tue, 12 Mar 2019 12:05:30 +0100 Subject: winrt: Implement manufacturer data Fixes: QTBUG-71943 Change-Id: Idb0524dde871d4aa153f0170f926ae76679f14cb Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothdevicediscoveryagent_p.h | 6 +- .../qbluetoothdevicediscoveryagent_winrt.cpp | 132 +++++++++++++++++---- src/bluetooth/qbluetoothdeviceinfo.h | 3 + 3 files changed, 117 insertions(+), 24 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h index e4370180..3e76c13d 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h @@ -81,6 +81,9 @@ QT_END_NAMESPACE #ifdef QT_WINRT_BLUETOOTH #include #include + +using ManufacturerData = QHash; +Q_DECLARE_METATYPE(ManufacturerData) #endif QT_BEGIN_NAMESPACE @@ -167,7 +170,8 @@ private: #ifdef QT_WINRT_BLUETOOTH private slots: void registerDevice(const QBluetoothDeviceInfo &info); - void updateDeviceRssi(const QBluetoothAddress &address, qint16 rssi); + void updateDeviceData(const QBluetoothAddress &address, QBluetoothDeviceInfo::Fields fields, + qint16 rssi, ManufacturerData manufacturerData); void onScanFinished(); private: diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp index 61f167de..1f96740c 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp @@ -52,6 +52,7 @@ #include #include +#include #include #include #include @@ -68,6 +69,7 @@ using namespace ABI::Windows::Devices; using namespace ABI::Windows::Devices::Bluetooth; using namespace ABI::Windows::Devices::Bluetooth::Advertisement; using namespace ABI::Windows::Devices::Enumeration; +using namespace ABI::Windows::Storage::Streams; QT_BEGIN_NAMESPACE @@ -79,6 +81,54 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT) ret; \ } +#define WARN_AND_CONTINUE_IF_FAILED(msg) \ + if (FAILED(hr)) { \ + qCWarning(QT_BT_WINRT) << msg; \ + continue; \ + } + +static QByteArray byteArrayFromBuffer(const ComPtr &buffer) +{ + ComPtr byteAccess; + HRESULT hr = buffer.As(&byteAccess); + Q_ASSERT_SUCCEEDED(hr); + char *data; + hr = byteAccess->Buffer(reinterpret_cast(&data)); + Q_ASSERT_SUCCEEDED(hr); + UINT32 size; + hr = buffer->get_Length(&size); + Q_ASSERT_SUCCEEDED(hr); + return QByteArray(data, int(size)); +} + + +static ManufacturerData extractManufacturerData(ComPtr ad) +{ + ManufacturerData ret; + ComPtr> data; + HRESULT hr = ad->get_ManufacturerData(&data); + WARN_AND_RETURN_IF_FAILED("Could not obtain list of manufacturer data.", return ret); + quint32 size; + hr = data->get_Size(&size); + WARN_AND_RETURN_IF_FAILED("Could not obtain manufacturer data's list size.", return ret); + for (quint32 i = 0; i < size; ++i) { + ComPtr d; + hr = data->GetAt(i, &d); + WARN_AND_CONTINUE_IF_FAILED("Could not obtain manufacturer data."); + quint16 id; + hr = d->get_CompanyId(&id); + WARN_AND_CONTINUE_IF_FAILED("Could not obtain manufacturer data company id."); + ComPtr buffer; + hr = d->get_Data(&buffer); + WARN_AND_CONTINUE_IF_FAILED("Could not obtain manufacturer data set."); + const QByteArray bufferData = byteArrayFromBuffer(buffer); + if (ret.contains(id)) + qCWarning(QT_BT_WINRT) << "Company ID already present in manufacturer data."; + ret.insert(id, bufferData); + } + return ret; +} + class QWinRTBluetoothDeviceDiscoveryWorker : public QObject { Q_OBJECT @@ -117,7 +167,8 @@ public slots: Q_SIGNALS: void deviceFound(const QBluetoothDeviceInfo &info); - void deviceRssiChanged(const QBluetoothAddress &address, qint16 rssi); + void deviceDataChanged(const QBluetoothAddress &address, QBluetoothDeviceInfo::Fields, + qint16 rssi, ManufacturerData manufacturerData); void scanFinished(); public: @@ -136,6 +187,7 @@ private: QMap m_foundLEDevicesMap; #endif QMap m_foundLEDevices; + QMap m_foundLEManufacturerData; int m_pendingPairedDevices; ComPtr m_deviceStatics; @@ -147,6 +199,8 @@ QWinRTBluetoothDeviceDiscoveryWorker::QWinRTBluetoothDeviceDiscoveryWorker(QBlue , m_pendingPairedDevices(0) { qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); #ifdef CLASSIC_APP_BUILD CoInitialize(NULL); @@ -281,11 +335,20 @@ void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher() qint16 rssi; hr = args->get_RawSignalStrengthInDBm(&rssi); Q_ASSERT_SUCCEEDED(hr); + ComPtr ad; + hr = args->get_Advertisement(&ad); + Q_ASSERT_SUCCEEDED(hr); + const ManufacturerData manufacturerData = extractManufacturerData(ad); + QBluetoothDeviceInfo::Fields changedFields = QBluetoothDeviceInfo::Field::None; + if (!m_foundLEManufacturerData.contains(address)) { + m_foundLEManufacturerData.insert(address, manufacturerData); + changedFields.setFlag(QBluetoothDeviceInfo::Field::ManufacturerData); + } else if (m_foundLEManufacturerData.value(address) != manufacturerData) { + m_foundLEManufacturerData[address] = manufacturerData; + changedFields.setFlag(QBluetoothDeviceInfo::Field::ManufacturerData); + } #if QT_CONFIG(winrt_btle_no_pairing) if (supportsNewLEApi()) { - ComPtr ad; - hr = args->get_Advertisement(&ad); - Q_ASSERT_SUCCEEDED(hr); ComPtr> guids; hr = ad->get_ServiceUuids(&guids); Q_ASSERT_SUCCEEDED(hr); @@ -307,10 +370,9 @@ void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher() return S_OK; const LEAdvertisingInfo adInfo = m_foundLEDevicesMap.value(address); QVector foundServices = adInfo.services; - bool rssiChanged = false; if (adInfo.rssi != rssi) { m_foundLEDevicesMap[address].rssi = rssi; - rssiChanged = true; + changedFields.setFlag(QBluetoothDeviceInfo::Field::RSSI); } bool newServiceAdded = false; for (const QBluetoothUuid &uuid : qAsConst(serviceUuids)) { @@ -320,10 +382,12 @@ void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher() } } if (!newServiceAdded) { - if (rssiChanged) { - QMetaObject::invokeMethod(this, "deviceRssiChanged", Qt::AutoConnection, + if (!changedFields.testFlag(QBluetoothDeviceInfo::Field::None)) { + QMetaObject::invokeMethod(this, "deviceDataChanged", Qt::AutoConnection, Q_ARG(QBluetoothAddress, QBluetoothAddress(address)), - Q_ARG(qint16, rssi)); + Q_ARG(QBluetoothDeviceInfo::Fields, changedFields), + Q_ARG(qint16, rssi), + Q_ARG(ManufacturerData, manufacturerData)); } return S_OK; } @@ -342,9 +406,14 @@ void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher() if (m_foundLEDevices.contains(address)) { if (m_foundLEDevices.value(address) != rssi) { m_foundLEDevices[address] = rssi; - QMetaObject::invokeMethod(this, "deviceRssiChanged", Qt::AutoConnection, + changedFields.setFlag(QBluetoothDeviceInfo::Field::RSSI); + } + if (!changedFields.testFlag(QBluetoothDeviceInfo::Field::None)) { + QMetaObject::invokeMethod(this, "deviceDataChanged", Qt::AutoConnection, Q_ARG(QBluetoothAddress, QBluetoothAddress(address)), - Q_ARG(qint16, rssi)); + Q_ARG(QBluetoothDeviceInfo::Fields, changedFields), + Q_ARG(qint16, rssi), + Q_ARG(ManufacturerData, manufacturerData)); } return S_OK; } @@ -649,14 +718,18 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtrstart(); @@ -832,16 +909,25 @@ void QBluetoothDeviceDiscoveryAgentPrivate::registerDevice(const QBluetoothDevic emit q->deviceDiscovered(info); } -void QBluetoothDeviceDiscoveryAgentPrivate::updateDeviceRssi(const QBluetoothAddress &address, qint16 rssi) +void QBluetoothDeviceDiscoveryAgentPrivate::updateDeviceData(const QBluetoothAddress &address, + QBluetoothDeviceInfo::Fields fields, + qint16 rssi, + ManufacturerData manufacturerData) { + if (fields.testFlag(QBluetoothDeviceInfo::Field::None)) + return; + Q_Q(QBluetoothDeviceDiscoveryAgent); for (QList::iterator iter = discoveredDevices.begin(); - iter != discoveredDevices.end(); ++iter) { + iter != discoveredDevices.end(); ++iter) { if (iter->address() == address) { - qCDebug(QT_BT_WINRT) << "Updating rssi for device" << iter->name() << iter->address() - << "from" << iter->rssi() << "to" << rssi; - iter->setRssi(rssi); - emit q->deviceUpdated(*iter, QBluetoothDeviceInfo::Field::RSSI); + qCDebug(QT_BT_WINRT) << "Updating data for device" << iter->name() << iter->address(); + if (fields.testFlag(QBluetoothDeviceInfo::Field::RSSI)) + iter->setRssi(rssi); + if (fields.testFlag(QBluetoothDeviceInfo::Field::ManufacturerData)) + for (quint16 key : manufacturerData.keys()) + iter->setManufacturerData(key, manufacturerData.value(key)); + emit q->deviceUpdated(*iter, fields); return; } } @@ -864,8 +950,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::disconnectAndClearWorker() this, &QBluetoothDeviceDiscoveryAgentPrivate::onScanFinished); disconnect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::deviceFound, q, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered); - disconnect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::deviceRssiChanged, - this, &QBluetoothDeviceDiscoveryAgentPrivate::updateDeviceRssi); + disconnect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::deviceDataChanged, + this, &QBluetoothDeviceDiscoveryAgentPrivate::updateDeviceData); if (leScanTimer) { disconnect(leScanTimer, &QTimer::timeout, worker, &QWinRTBluetoothDeviceDiscoveryWorker::finishDiscovery); diff --git a/src/bluetooth/qbluetoothdeviceinfo.h b/src/bluetooth/qbluetoothdeviceinfo.h index db0de7cd..11cb2bea 100644 --- a/src/bluetooth/qbluetoothdeviceinfo.h +++ b/src/bluetooth/qbluetoothdeviceinfo.h @@ -284,5 +284,8 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QBluetoothDeviceInfo::ServiceClasses) QT_END_NAMESPACE Q_DECLARE_METATYPE(QBluetoothDeviceInfo) +#ifdef QT_WINRT_BLUETOOTH +Q_DECLARE_METATYPE(QBluetoothDeviceInfo::Fields) +#endif #endif -- cgit v1.2.3 From ca0841c0d5f32baf6bc13c80eb801f24aa74cef1 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Tue, 12 Mar 2019 12:06:54 +0100 Subject: winrt: Use correct worker signal slot disconnection Change-Id: I41929e01b79ca0eacf80fcb1d5cead2146eb2e8e Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp index 1f96740c..5dad5ee7 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp @@ -942,19 +942,18 @@ void QBluetoothDeviceDiscoveryAgentPrivate::onScanFinished() void QBluetoothDeviceDiscoveryAgentPrivate::disconnectAndClearWorker() { - Q_Q(QBluetoothDeviceDiscoveryAgent); if (!worker) return; disconnect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::scanFinished, - this, &QBluetoothDeviceDiscoveryAgentPrivate::onScanFinished); + this, &QBluetoothDeviceDiscoveryAgentPrivate::onScanFinished); disconnect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::deviceFound, - q, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered); + this, &QBluetoothDeviceDiscoveryAgentPrivate::registerDevice); disconnect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::deviceDataChanged, this, &QBluetoothDeviceDiscoveryAgentPrivate::updateDeviceData); if (leScanTimer) { disconnect(leScanTimer, &QTimer::timeout, - worker, &QWinRTBluetoothDeviceDiscoveryWorker::finishDiscovery); + worker, &QWinRTBluetoothDeviceDiscoveryWorker::finishDiscovery); } worker.clear(); } -- cgit v1.2.3 From 929d7446ead35b481fab8e5fef6cc889225f1143 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Thu, 14 Mar 2019 14:39:02 +0100 Subject: winrt: Handle device disconnection properly "ConnectedState" is not the only valid "connected" state for QLowEnergyController. "disconnected" also has to be emitted, if we were in DiscoveredState and co. Additionally every value change callback has to be removed and mDevice has to be closed in disconnectFromDevice to make Windows disconnect from the device as there is no specific API to do that manually. Task-number: QTBUG-74394 Change-Id: Ic025b2c668f887db6c4a631cd8fee26a457bc279 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt.cpp | 27 ++++++++++++++++++------ src/bluetooth/qlowenergycontroller_winrt_new.cpp | 27 ++++++++++++++++++------ src/bluetooth/qlowenergycontroller_winrt_new_p.h | 1 + src/bluetooth/qlowenergycontroller_winrt_p.h | 1 + 4 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt.cpp b/src/bluetooth/qlowenergycontroller_winrt.cpp index ed279ffa..5347902f 100644 --- a/src/bluetooth/qlowenergycontroller_winrt.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt.cpp @@ -291,9 +291,7 @@ QLowEnergyControllerPrivateWinRT::~QLowEnergyControllerPrivateWinRT() if (mDevice && mStatusChangedToken.value) mDevice->remove_ConnectionStatusChanged(mStatusChangedToken); - qCDebug(QT_BT_WINRT) << "Unregistering " << mValueChangedTokens.count() << " value change tokens"; - for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens)) - entry.characteristic->remove_ValueChanged(entry.token); + unregisterFromValueChanges(); } void QLowEnergyControllerPrivateWinRT::init() @@ -340,7 +338,7 @@ void QLowEnergyControllerPrivateWinRT::connectToDevice() && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) { setState(QLowEnergyController::ConnectedState); emit q->connected(); - } else if (state == QLowEnergyController::ConnectedState + } else if (state != QLowEnergyController::UnconnectedState && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) { setError(QLowEnergyController::RemoteHostClosedError); setState(QLowEnergyController::UnconnectedState); @@ -438,9 +436,13 @@ void QLowEnergyControllerPrivateWinRT::disconnectFromDevice() Q_Q(QLowEnergyController); setState(QLowEnergyController::UnconnectedState); emit q->disconnected(); - if (mDevice && mStatusChangedToken.value) { - mDevice->remove_ConnectionStatusChanged(mStatusChangedToken); - mStatusChangedToken.value = 0; + unregisterFromValueChanges(); + if (mDevice) { + if (mStatusChangedToken.value) { + mDevice->remove_ConnectionStatusChanged(mStatusChangedToken); + mStatusChangedToken.value = 0; + } + mDevice = nullptr; } } @@ -502,6 +504,17 @@ void QLowEnergyControllerPrivateWinRT::registerForValueChanges(const QBluetoothU << serviceUuid << "registered for value changes"; } +void QLowEnergyControllerPrivateWinRT::unregisterFromValueChanges() +{ + qCDebug(QT_BT_WINRT) << "Unregistering " << mValueChangedTokens.count() << " value change tokens"; + HRESULT hr; + for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens)) { + hr = entry.characteristic->remove_ValueChanged(entry.token); + Q_ASSERT_SUCCEEDED(hr); + } + mValueChangedTokens.clear(); +} + void QLowEnergyControllerPrivateWinRT::obtainIncludedServices(QSharedPointer servicePointer, ComPtr service) { diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index 1f70807e..df00d4c8 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -345,9 +345,7 @@ QLowEnergyControllerPrivateWinRTNew::~QLowEnergyControllerPrivateWinRTNew() if (mDevice && mStatusChangedToken.value) mDevice->remove_ConnectionStatusChanged(mStatusChangedToken); - qCDebug(QT_BT_WINRT) << "Unregistering " << mValueChangedTokens.count() << " value change tokens"; - for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens)) - entry.characteristic->remove_ValueChanged(entry.token); + unregisterFromValueChanges(); } void QLowEnergyControllerPrivateWinRTNew::init() @@ -397,7 +395,7 @@ void QLowEnergyControllerPrivateWinRTNew::connectToDevice() && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) { setState(QLowEnergyController::ConnectedState); emit q->connected(); - } else if (state == QLowEnergyController::ConnectedState + } else if (state != QLowEnergyController::UnconnectedState && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) { setError(QLowEnergyController::RemoteHostClosedError); setState(QLowEnergyController::UnconnectedState); @@ -522,9 +520,13 @@ void QLowEnergyControllerPrivateWinRTNew::disconnectFromDevice() Q_Q(QLowEnergyController); setState(QLowEnergyController::UnconnectedState); emit q->disconnected(); - if (mDevice && mStatusChangedToken.value) { - mDevice->remove_ConnectionStatusChanged(mStatusChangedToken); - mStatusChangedToken.value = 0; + unregisterFromValueChanges(); + if (mDevice) { + if (mStatusChangedToken.value) { + mDevice->remove_ConnectionStatusChanged(mStatusChangedToken); + mStatusChangedToken.value = 0; + } + mDevice = nullptr; } } @@ -591,6 +593,17 @@ void QLowEnergyControllerPrivateWinRTNew::registerForValueChanges(const QBluetoo << serviceUuid << "registered for value changes"; } +void QLowEnergyControllerPrivateWinRTNew::unregisterFromValueChanges() +{ + qCDebug(QT_BT_WINRT) << "Unregistering " << mValueChangedTokens.count() << " value change tokens"; + HRESULT hr; + for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens)) { + hr = entry.characteristic->remove_ValueChanged(entry.token); + Q_ASSERT_SUCCEEDED(hr); + } + mValueChangedTokens.clear(); +} + void QLowEnergyControllerPrivateWinRTNew::obtainIncludedServices( QSharedPointer servicePointer, ComPtr service) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new_p.h b/src/bluetooth/qlowenergycontroller_winrt_new_p.h index 716d2d07..b3990acd 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new_p.h +++ b/src/bluetooth/qlowenergycontroller_winrt_new_p.h @@ -140,6 +140,7 @@ private: Microsoft::WRL::ComPtr getNativeCharacteristic(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid); void registerForValueChanges(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid); + void unregisterFromValueChanges(); void obtainIncludedServices(QSharedPointer servicePointer, Microsoft::WRL::ComPtr nativeService); diff --git a/src/bluetooth/qlowenergycontroller_winrt_p.h b/src/bluetooth/qlowenergycontroller_winrt_p.h index 783a71fa..da21353f 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_p.h +++ b/src/bluetooth/qlowenergycontroller_winrt_p.h @@ -138,6 +138,7 @@ private: Microsoft::WRL::ComPtr getNativeCharacteristic(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid); void registerForValueChanges(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid); + void unregisterFromValueChanges(); void obtainIncludedServices(QSharedPointer servicePointer, Microsoft::WRL::ComPtr nativeService); -- cgit v1.2.3 From ed4f9f3c4354d4f65318832eadaf0cfc5c288404 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Fri, 15 Mar 2019 10:15:57 +0100 Subject: winrt: Stop LE device watcher when discovery is finished In order to protect ourselves from late callbacks we have to unregister the LE watcher's callback as soon as we signal, that we are done with device discovery. We do not expect any additional devices afterwards and might run into a late callback otherwise, so we should stop the watcher immediately and not wait until worker destruction. Change-Id: I85520a4724397aeb5a693d2c8b940a72d68d9663 Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp index 5dad5ee7..e0209693 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp @@ -136,7 +136,7 @@ public: explicit QWinRTBluetoothDeviceDiscoveryWorker(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods); ~QWinRTBluetoothDeviceDiscoveryWorker(); void start(); - void stop(); + void stopLEWatcher(); private: void startDeviceDiscovery(QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode); @@ -213,7 +213,7 @@ QWinRTBluetoothDeviceDiscoveryWorker::QWinRTBluetoothDeviceDiscoveryWorker(QBlue QWinRTBluetoothDeviceDiscoveryWorker::~QWinRTBluetoothDeviceDiscoveryWorker() { - stop(); + stopLEWatcher(); #ifdef CLASSIC_APP_BUILD CoUninitialize(); #endif @@ -235,7 +235,7 @@ void QWinRTBluetoothDeviceDiscoveryWorker::start() qCDebug(QT_BT_WINRT) << "Worker started"; } -void QWinRTBluetoothDeviceDiscoveryWorker::stop() +void QWinRTBluetoothDeviceDiscoveryWorker::stopLEWatcher() { if (m_leWatcher) { HRESULT hr = m_leWatcher->Stop(); @@ -430,6 +430,7 @@ void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher() void QWinRTBluetoothDeviceDiscoveryWorker::finishDiscovery() { emit scanFinished(); + stopLEWatcher(); deleteLater(); } @@ -875,7 +876,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop() { Q_Q(QBluetoothDeviceDiscoveryAgent); if (worker) { - worker->stop(); + worker->stopLEWatcher(); disconnectAndClearWorker(); emit q->canceled(); } -- cgit v1.2.3 From 4747a72d9a69f53760793ce0c0bb0154e43eca45 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Fri, 15 Mar 2019 13:42:17 +0100 Subject: winrt: Fix notifications in Bluetooth LE We have to register callbacks for every indicatable characteristics we encounter during lookup so we have to store our list for the whole lookup period. Previously the list of indicatable characteristics was recreated in every "Completed" handler of Characteristic::GetDescriptorsAsync. The result was, that at most 1 indicatable characteristic was found. That behavior is wrong and we have to store the list of indicatable characteristics through the whole obtainCharList call. Fixes: QTBUG-74394 Change-Id: Ie93d9bd0184686d2d7cf9b082e85cf89ca5493ce Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index df00d4c8..95acfbb9 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -138,7 +138,7 @@ public: public slots: void obtainCharList() { - QVector indicateChars; + mIndicateChars.clear(); quint16 startHandle = 0; quint16 endHandle = 0; qCDebug(QT_BT_WINRT) << __FUNCTION__; @@ -146,7 +146,7 @@ public slots: HRESULT hr = mDeviceService->GetAllCharacteristics(&characteristics); Q_ASSERT_SUCCEEDED(hr); if (!characteristics) { - emit charListObtained(mService, mCharacteristicList, indicateChars, startHandle, endHandle); + emit charListObtained(mService, mCharacteristicList, mIndicateChars, startHandle, endHandle); QThread::currentThread()->quit(); return; } @@ -235,7 +235,6 @@ public slots: charData.value = byteArrayFromGattResult(readResult); } - QVector indicateChars; ComPtr> descriptors; ComPtr characteristic2; @@ -289,7 +288,7 @@ public slots: descData.value = QByteArray(2, Qt::Uninitialized); qToLittleEndian(result, descData.value.data()); - indicateChars << charData.uuid; + mIndicateChars << charData.uuid; } else { ComPtr> readOp; hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, @@ -309,7 +308,7 @@ public slots: mCharacteristicList.insert(handle, charData); mCharacteristicsCountToBeDiscovered--; if (mCharacteristicsCountToBeDiscovered == 0) { - emit charListObtained(mService, mCharacteristicList, indicateChars, + emit charListObtained(mService, mCharacteristicList, mIndicateChars, mStartHandle, mEndHandle); QThread::currentThread()->quit(); } @@ -326,6 +325,7 @@ public: uint mCharacteristicsCountToBeDiscovered; quint16 mStartHandle = 0; quint16 mEndHandle = 0; + QVector mIndicateChars; signals: void charListObtained(const QBluetoothUuid &service, QHash Date: Tue, 20 Feb 2018 14:37:49 +0300 Subject: LE/Android: add consistency check in method called from Qt API Call to includedServices() from Qt side after unexpected disconnection leads to NullPointerException due to unchecked member access. Fix that! Change-Id: Iae89801d7af86d5a3f34ebba9eb2ea12da252cd2 Reviewed-by: Alex Blasche --- .../src/org/qtproject/qt5/android/bluetooth/QtBluetoothLE.java | 3 +++ 1 file changed, 3 insertions(+) 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 dc48514a..6c548f84 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 @@ -839,6 +839,9 @@ public class QtBluetoothLE { */ public String includedServices(String serviceUuid) { + if (mBluetoothGatt == null) + return null; + UUID uuid; try { uuid = UUID.fromString(serviceUuid); -- cgit v1.2.3 From 16f033fb13a3005eefbe8b7b6ccc52bfda08649e Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Tue, 19 Mar 2019 11:21:19 +0100 Subject: Address compile error on gcc 4.8 This partly reverts b3b387698e8f685e8bd4f4843a170da40e17249b. The global static connect was not correct. Furthermore the new global static variable was triggering src/nfc/qnearfieldtarget_emulator_p.h:125:31: warning: 'globalTagActivator' defined but not used [-Wunused-variable] The global static variable is shifted to no longer require exposure inside the header file. Fixes: QTBUG-74538 Change-Id: Ic5e8211d358bae9c2ed0418aec5c4316fb249c98 Reviewed-by: Oliver Wolff --- src/nfc/qnearfieldmanager_emulator.cpp | 9 +++++---- src/nfc/qnearfieldtarget_emulator.cpp | 7 +++++++ src/nfc/qnearfieldtarget_emulator_p.h | 2 -- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/nfc/qnearfieldmanager_emulator.cpp b/src/nfc/qnearfieldmanager_emulator.cpp index 8a61a3a9..4b5e5e0c 100644 --- a/src/nfc/qnearfieldmanager_emulator.cpp +++ b/src/nfc/qnearfieldmanager_emulator.cpp @@ -49,10 +49,11 @@ QT_BEGIN_NAMESPACE QNearFieldManagerPrivateImpl::QNearFieldManagerPrivateImpl() { - globalTagActivator->initialize(); + TagActivator *activator = TagActivator::instance(); + activator->initialize(); - connect(globalTagActivator, &TagActivator::tagActivated, this, &QNearFieldManagerPrivateImpl::tagActivated); - connect(globalTagActivator, &TagActivator::tagDeactivated, this, &QNearFieldManagerPrivateImpl::tagDeactivated); + connect(activator, &TagActivator::tagActivated, this, &QNearFieldManagerPrivateImpl::tagActivated); + connect(activator, &TagActivator::tagDeactivated, this, &QNearFieldManagerPrivateImpl::tagDeactivated); } QNearFieldManagerPrivateImpl::~QNearFieldManagerPrivateImpl() @@ -66,7 +67,7 @@ bool QNearFieldManagerPrivateImpl::isAvailable() const void QNearFieldManagerPrivateImpl::reset() { - globalTagActivator->reset(); + TagActivator::instance()->reset(); } void QNearFieldManagerPrivateImpl::tagActivated(TagBase *tag) diff --git a/src/nfc/qnearfieldtarget_emulator.cpp b/src/nfc/qnearfieldtarget_emulator.cpp index 0723b655..aecd743e 100644 --- a/src/nfc/qnearfieldtarget_emulator.cpp +++ b/src/nfc/qnearfieldtarget_emulator.cpp @@ -52,6 +52,8 @@ QT_BEGIN_NAMESPACE static QMutex tagMutex; static QMap tagMap; +Q_GLOBAL_STATIC(TagActivator, globalTagActivator); + TagType1::TagType1(TagBase *tag, QObject *parent) : QNearFieldTagType1(parent), m_tag(tag) { @@ -247,6 +249,11 @@ void TagActivator::reset() tagMap.clear(); } +TagActivator *TagActivator::instance() +{ + return globalTagActivator(); +} + void TagActivator::timerEvent(QTimerEvent *e) { Q_UNUSED(e); diff --git a/src/nfc/qnearfieldtarget_emulator_p.h b/src/nfc/qnearfieldtarget_emulator_p.h index 1b9f7bdb..70a67be8 100644 --- a/src/nfc/qnearfieldtarget_emulator_p.h +++ b/src/nfc/qnearfieldtarget_emulator_p.h @@ -122,8 +122,6 @@ private: int timerId; }; -Q_GLOBAL_STATIC(TagActivator, globalTagActivator); - QT_END_NAMESPACE #endif // QNEARFIELDTARGET_EMULATOR_P_H -- cgit v1.2.3 From c488aedb42cda76085cfc0ed2602c34c3b5353a6 Mon Sep 17 00:00:00 2001 From: Kari Oikarinen Date: Thu, 21 Mar 2019 09:08:20 +0200 Subject: Bump version Change-Id: I9bfd6c6128289f20221f8cb0176cbc527997490e --- .qmake.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.qmake.conf b/.qmake.conf index c954a094..1bf9543a 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,3 +1,3 @@ load(qt_build_config) -MODULE_VERSION = 5.12.2 +MODULE_VERSION = 5.12.3 -- cgit v1.2.3 From 8d513a3a2006efd3e0ca20110e2c4b82045dbfe9 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Tue, 19 Mar 2019 10:40:06 +0100 Subject: winrt: Fix deprecation warning IGattCharacteristic::GetAllDescriptor is deprecated and thus should not be used. As we are calling GetDescriptorsAsync anyways and are inside the Completed callback of that function we should use its result anyways. Change-Id: I362f860d66da75dd899ed89604e426952ce9fe1b Reviewed-by: Miguel Costa Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index 95acfbb9..42451e35 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -200,7 +200,8 @@ public slots: Q_ASSERT_SUCCEEDED(hr); hr = descAsyncResult->put_Completed( Callback>( - [this, characteristic](IAsyncOperation *, AsyncStatus status) { + [this, characteristic](IAsyncOperation *op, + AsyncStatus status) { if (status != AsyncStatus::Completed) { qCDebug(QT_BT_WINRT) << "Could not obtain descriptors"; return S_OK; @@ -237,14 +238,12 @@ public slots: ComPtr> descriptors; - ComPtr characteristic2; - hr = characteristic.As(&characteristic2); + ComPtr result; + hr = op->GetResults(&result); Q_ASSERT_SUCCEEDED(hr); - - hr = characteristic2->GetAllDescriptors(&descriptors); + hr = result->get_Descriptors(&descriptors); Q_ASSERT_SUCCEEDED(hr); - uint descriptorCount; hr = descriptors->get_Size(&descriptorCount); Q_ASSERT_SUCCEEDED(hr); -- cgit v1.2.3 From 75648df8fa91fc0918285988e75ec996f0dc0f97 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Mon, 25 Mar 2019 11:15:29 +0100 Subject: qlowenergycontroller_android_p.h: Remove unrelated preprocessor blocks Change-Id: I964806ccdb3bd39a33b860d496ef05f8b96135db Reviewed-by: Timur Pocheptsov --- src/bluetooth/qlowenergycontroller_android_p.h | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_android_p.h b/src/bluetooth/qlowenergycontroller_android_p.h index 3f97e363..f05c63ca 100644 --- a/src/bluetooth/qlowenergycontroller_android_p.h +++ b/src/bluetooth/qlowenergycontroller_android_p.h @@ -60,15 +60,8 @@ #include "qlowenergycontroller.h" #include "qlowenergycontrollerbase_p.h" -#if QT_CONFIG(bluez) && !defined(QT_BLUEZ_NO_BTLE) -#include -#elif defined(QT_ANDROID_BLUETOOTH) #include #include "android/lowenergynotificationhub_p.h" -#elif defined(QT_WINRT_BLUETOOTH) -#include -#include -#endif #include @@ -77,16 +70,7 @@ QT_BEGIN_NAMESPACE class QLowEnergyServiceData; class QTimer; -#if QT_CONFIG(bluez) && !defined(QT_BLUEZ_NO_BTLE) -class HciManager; -class LeCmacCalculator; -class QSocketNotifier; -class RemoteDeviceManager; -#elif defined(QT_ANDROID_BLUETOOTH) class LowEnergyNotificationHub; -#elif defined(QT_WINRT_BLUETOOTH) -class QWinRTLowEnergyServiceHandler; -#endif extern void registerQLowEnergyControllerMetaType(); -- cgit v1.2.3 From 6f442044a17ae8c2acb201ba8c9eb1a8cb452899 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Mon, 25 Mar 2019 11:08:35 +0100 Subject: Remove winrt TODOs about manufacturer data Task-number: QTBUG-71943 Change-Id: I35f9aedb0615d8341f93f67b4549d1e9a03c9cb9 Reviewed-by: Timur Pocheptsov --- src/bluetooth/qbluetoothdevicediscoveryagent.cpp | 2 -- src/bluetooth/qbluetoothdeviceinfo.cpp | 1 - 2 files changed, 3 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent.cpp index b132a3a6..fb14850e 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent.cpp @@ -176,8 +176,6 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT) \sa QBluetoothDeviceInfo::rssi(), lowEnergyDiscoveryTimeout() */ -// TODO deviceUpdated() signal not implemented on WinRT - /*! \fn void QBluetoothDeviceDiscoveryAgent::finished() diff --git a/src/bluetooth/qbluetoothdeviceinfo.cpp b/src/bluetooth/qbluetoothdeviceinfo.cpp index cc0d98a4..46df5c7b 100644 --- a/src/bluetooth/qbluetoothdeviceinfo.cpp +++ b/src/bluetooth/qbluetoothdeviceinfo.cpp @@ -647,7 +647,6 @@ QVector QBluetoothDeviceInfo::manufacturerIds() const */ QByteArray QBluetoothDeviceInfo::manufacturerData(quint16 manufacturerId) const { - // TODO Currently not implemented on WinRT Q_D(const QBluetoothDeviceInfo); return d->manufacturerData.value(manufacturerId); } -- cgit v1.2.3 From 043145014565dad540b416a0b369a7d04630e649 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Wed, 20 Mar 2019 11:11:01 +0100 Subject: Update SensorTag documentation URL Change-Id: Ia97bc59eb1d26aeba00d1c729b327c2eb857987c Reviewed-by: Oliver Wolff --- tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp b/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp index b175ddd0..8ffc0480 100644 --- a/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp +++ b/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp @@ -1755,7 +1755,8 @@ void tst_QLowEnergyController::tst_writeCharacteristic() QTRY_VERIFY_WITH_TIMEOUT( service->state() == QLowEnergyService::ServiceDiscovered, 30000); - //test service described by http://processors.wiki.ti.com/index.php/SensorTag_User_Guide + // test service described by + // http://processors.wiki.ti.com/index.php/CC2650_SensorTag_User%27s_Guide const QList chars = service->characteristics(); QLowEnergyCharacteristic dataChar; @@ -1929,7 +1930,7 @@ void tst_QLowEnergyController::tst_readWriteDescriptor() service->state() == QLowEnergyService::ServiceDiscovered, 30000); // Temperature service described by - // http://processors.wiki.ti.com/index.php/SensorTag_User_Guide + // http://processors.wiki.ti.com/index.php/CC2650_SensorTag_User%27s_Guide // 1. Find temperature data characteristic const QLowEnergyCharacteristic tempData = service->characteristic( -- cgit v1.2.3 From 545b282d6d4a3c66e00c41ffd83c1a69f6fd0ad2 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Wed, 20 Mar 2019 10:58:21 +0100 Subject: Ensure DBus based QBluetoothSocket emits bytesWritten() signal [ChangeLog][QtBluetooth][Linux/BlueZ] Fixed missing emission of QBluetoothSocket::bytesWritten() signal on Bluez v5.46+. Fixes: QTBUG-74513 Change-Id: I93cb5abe65e13f6a5cc5bb195cc98526a507916a Reviewed-by: Timur Pocheptsov --- src/bluetooth/qbluetoothsocket_bluezdbus.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bluetooth/qbluetoothsocket_bluezdbus.cpp b/src/bluetooth/qbluetoothsocket_bluezdbus.cpp index c98d0c26..d3fc13e4 100644 --- a/src/bluetooth/qbluetoothsocket_bluezdbus.cpp +++ b/src/bluetooth/qbluetoothsocket_bluezdbus.cpp @@ -538,6 +538,8 @@ void QBluetoothSocketPrivateBluezDBus::remoteConnected(const QDBusUnixFileDescri q, &QBluetoothSocket::readyRead); connect(localSocket, &QLocalSocket::stateChanged, this, &QBluetoothSocketPrivateBluezDBus::socketStateChanged); + connect(localSocket, &QLocalSocket::bytesWritten, + q, &QBluetoothSocket::bytesWritten); socket = descriptor; q->setSocketState(QBluetoothSocket::ConnectedState); -- cgit v1.2.3 From d21da7829811d129b96ca0eb2a56cb6bdae4f1a7 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Wed, 20 Mar 2019 10:44:49 +0100 Subject: Convert bttestui to Qt5 style connect() syntax Change-Id: Ib110da9e0a28fe54b12bcfe92d1e58447a42328e Reviewed-by: Timur Pocheptsov --- tests/bttestui/btlocaldevice.cpp | 90 ++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/tests/bttestui/btlocaldevice.cpp b/tests/bttestui/btlocaldevice.cpp index 583a8c6b..fb3a0270 100644 --- a/tests/bttestui/btlocaldevice.cpp +++ b/tests/bttestui/btlocaldevice.cpp @@ -49,56 +49,56 @@ BtLocalDevice::BtLocalDevice(QObject *parent) : QObject(parent), securityFlags(QBluetooth::NoSecurity) { localDevice = new QBluetoothLocalDevice(this); - connect(localDevice, SIGNAL(error(QBluetoothLocalDevice::Error)), - this, SIGNAL(error(QBluetoothLocalDevice::Error))); - connect(localDevice, SIGNAL(hostModeStateChanged(QBluetoothLocalDevice::HostMode)), - this, SIGNAL(hostModeStateChanged())); - connect(localDevice, SIGNAL(pairingFinished(QBluetoothAddress,QBluetoothLocalDevice::Pairing)), - this, SLOT(pairingFinished(QBluetoothAddress,QBluetoothLocalDevice::Pairing))); - connect(localDevice, SIGNAL(deviceConnected(QBluetoothAddress)), - this, SLOT(connected(QBluetoothAddress))); - connect(localDevice, SIGNAL(deviceDisconnected(QBluetoothAddress)), - this, SLOT(disconnected(QBluetoothAddress))); - connect(localDevice, SIGNAL(pairingDisplayConfirmation(QBluetoothAddress,QString)), - this, SLOT(pairingDisplayConfirmation(QBluetoothAddress,QString))); + connect(localDevice, &QBluetoothLocalDevice::error, + this, &BtLocalDevice::error); + connect(localDevice, &QBluetoothLocalDevice::hostModeStateChanged, + this, &BtLocalDevice::hostModeStateChanged); + connect(localDevice, &QBluetoothLocalDevice::pairingFinished, + this, &BtLocalDevice::pairingFinished); + connect(localDevice, &QBluetoothLocalDevice::deviceConnected, + this, &BtLocalDevice::connected); + connect(localDevice, &QBluetoothLocalDevice::deviceDisconnected, + this, &BtLocalDevice::disconnected); + connect(localDevice, &QBluetoothLocalDevice::pairingDisplayConfirmation, + this, &BtLocalDevice::pairingDisplayConfirmation); connect(localDevice, &QBluetoothLocalDevice::pairingDisplayPinCode, this, &BtLocalDevice::pairingDisplayPinCode); if (localDevice->isValid()) { deviceAgent = new QBluetoothDeviceDiscoveryAgent(this); - connect(deviceAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)), - this, SLOT(deviceDiscovered(QBluetoothDeviceInfo))); - connect(deviceAgent, SIGNAL(finished()), - this, SLOT(discoveryFinished())); - connect(deviceAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)), - this, SLOT(discoveryError(QBluetoothDeviceDiscoveryAgent::Error))); - connect(deviceAgent, SIGNAL(canceled()), - this, SLOT(discoveryCanceled())); + connect(deviceAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, + this, &BtLocalDevice::deviceDiscovered); + connect(deviceAgent, &QBluetoothDeviceDiscoveryAgent::finished, + this, &BtLocalDevice::discoveryFinished); + connect(deviceAgent, QOverload::of(&QBluetoothDeviceDiscoveryAgent::error), + this, &BtLocalDevice::discoveryError); + connect(deviceAgent, &QBluetoothDeviceDiscoveryAgent::canceled, + this, &BtLocalDevice::discoveryCanceled); serviceAgent = new QBluetoothServiceDiscoveryAgent(this); - connect(serviceAgent, SIGNAL(serviceDiscovered(QBluetoothServiceInfo)), - this, SLOT(serviceDiscovered(QBluetoothServiceInfo))); - connect(serviceAgent, SIGNAL(finished()), - this, SLOT(serviceDiscoveryFinished())); - connect(serviceAgent, SIGNAL(canceled()), - this, SLOT(serviceDiscoveryCanceled())); - connect(serviceAgent, SIGNAL(error(QBluetoothServiceDiscoveryAgent::Error)), - this, SLOT(serviceDiscoveryError(QBluetoothServiceDiscoveryAgent::Error))); + connect(serviceAgent, &QBluetoothServiceDiscoveryAgent::serviceDiscovered, + this, &BtLocalDevice::serviceDiscovered); + connect(serviceAgent, &QBluetoothServiceDiscoveryAgent::finished, + this, &BtLocalDevice::serviceDiscoveryFinished); + connect(serviceAgent, &QBluetoothServiceDiscoveryAgent::canceled, + this, &BtLocalDevice::serviceDiscoveryCanceled); + connect(serviceAgent, QOverload::of(&QBluetoothServiceDiscoveryAgent::error), + this, &BtLocalDevice::serviceDiscoveryError); socket = new QBluetoothSocket(SOCKET_PROTOCOL, this); - connect(socket, SIGNAL(stateChanged(QBluetoothSocket::SocketState)), - this, SLOT(socketStateChanged(QBluetoothSocket::SocketState))); - connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), - this, SLOT(socketError(QBluetoothSocket::SocketError))); - connect(socket, SIGNAL(connected()), this, SLOT(socketConnected())); - connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); - connect(socket, SIGNAL(readyRead()), this, SLOT(readData())); + connect(socket, &QBluetoothSocket::stateChanged, + this, &BtLocalDevice::socketStateChanged); + connect(socket, QOverload::of(&QBluetoothSocket::error), + this, &BtLocalDevice::socketError); + connect(socket, &QBluetoothSocket::connected, this, &BtLocalDevice::socketConnected); + connect(socket, &QBluetoothSocket::disconnected, this, &BtLocalDevice::socketDisconnected); + connect(socket, &QIODevice::readyRead, this, &BtLocalDevice::readData); setSecFlags(static_cast(socket->preferredSecurityFlags())); server = new QBluetoothServer(SOCKET_PROTOCOL, this); - connect(server, SIGNAL(newConnection()), this, SLOT(serverNewConnection())); - connect(server, SIGNAL(error(QBluetoothServer::Error)), - this, SLOT(serverError(QBluetoothServer::Error))); + connect(server, &QBluetoothServer::newConnection, this, &BtLocalDevice::serverNewConnection); + connect(server, QOverload::of(&QBluetoothServer::error), + this, &BtLocalDevice::serverError); } else { deviceAgent = nullptr; serviceAgent = nullptr; @@ -675,13 +675,13 @@ void BtLocalDevice::serverNewConnection() } client->setParent(this); - connect(client, SIGNAL(disconnected()), this, SLOT(clientSocketDisconnected())); - connect(client, SIGNAL(readyRead()), this, SLOT(clientSocketReadyRead())); - connect(client, SIGNAL(stateChanged(QBluetoothSocket::SocketState)), - this, SLOT(socketStateChanged(QBluetoothSocket::SocketState))); - connect(client, SIGNAL(error(QBluetoothSocket::SocketError)), - this, SLOT(socketError(QBluetoothSocket::SocketError))); - connect(client, SIGNAL(connected()), this, SLOT(socketConnected())); + connect(client, &QBluetoothSocket::disconnected, this, &BtLocalDevice::clientSocketDisconnected); + connect(client, &QIODevice::readyRead, this, &BtLocalDevice::clientSocketReadyRead); + connect(client, &QBluetoothSocket::stateChanged, + this, &BtLocalDevice::socketStateChanged); + connect(client, QOverload::of(&QBluetoothSocket::error), + this, &BtLocalDevice::socketError); + connect(client, &QBluetoothSocket::connected, this, &BtLocalDevice::socketConnected); serverSockets.append(client); } -- cgit v1.2.3 From 845e185b2881210b8f3a0dc25926c3d74b0008bf Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Mon, 25 Mar 2019 15:46:11 +0100 Subject: winrt: More corrections for handling device disconnections The signal about device disconnections has to be done at the very end of disconnectFromDevice as a user's application might access an invalid mDevice otherwise if it tries to reconnect automatically. The list of device services has to be invalidated when a remote device disconnects. The same is done in other backends. Additionally the "valueChanged" callbacks should be unregistered on device disconnection. Thus we avoid late callbacks which might cause asserts. Task-number: QTBUG-74394 Change-Id: I156be8ebefc9d9e5533bc60e7018088641680ba3 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt.cpp | 7 +++++-- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt.cpp b/src/bluetooth/qlowenergycontroller_winrt.cpp index 5347902f..989c5443 100644 --- a/src/bluetooth/qlowenergycontroller_winrt.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt.cpp @@ -340,6 +340,8 @@ void QLowEnergyControllerPrivateWinRT::connectToDevice() emit q->connected(); } else if (state != QLowEnergyController::UnconnectedState && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) { + invalidateServices(); + unregisterFromValueChanges(); setError(QLowEnergyController::RemoteHostClosedError); setState(QLowEnergyController::UnconnectedState); emit q->disconnected(); @@ -434,8 +436,7 @@ void QLowEnergyControllerPrivateWinRT::disconnectFromDevice() { qCDebug(QT_BT_WINRT) << __FUNCTION__; Q_Q(QLowEnergyController); - setState(QLowEnergyController::UnconnectedState); - emit q->disconnected(); + setState(QLowEnergyController::ClosingState); unregisterFromValueChanges(); if (mDevice) { if (mStatusChangedToken.value) { @@ -444,6 +445,8 @@ void QLowEnergyControllerPrivateWinRT::disconnectFromDevice() } mDevice = nullptr; } + setState(QLowEnergyController::UnconnectedState); + emit q->disconnected(); } ComPtr QLowEnergyControllerPrivateWinRT::getNativeService(const QBluetoothUuid &serviceUuid) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index 42451e35..42d0a320 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -396,6 +396,8 @@ void QLowEnergyControllerPrivateWinRTNew::connectToDevice() emit q->connected(); } else if (state != QLowEnergyController::UnconnectedState && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) { + invalidateServices(); + unregisterFromValueChanges(); setError(QLowEnergyController::RemoteHostClosedError); setState(QLowEnergyController::UnconnectedState); emit q->disconnected(); @@ -517,8 +519,7 @@ void QLowEnergyControllerPrivateWinRTNew::disconnectFromDevice() { qCDebug(QT_BT_WINRT) << __FUNCTION__; Q_Q(QLowEnergyController); - setState(QLowEnergyController::UnconnectedState); - emit q->disconnected(); + setState(QLowEnergyController::ClosingState); unregisterFromValueChanges(); if (mDevice) { if (mStatusChangedToken.value) { @@ -527,6 +528,8 @@ void QLowEnergyControllerPrivateWinRTNew::disconnectFromDevice() } mDevice = nullptr; } + setState(QLowEnergyController::UnconnectedState); + emit q->disconnected(); } ComPtr QLowEnergyControllerPrivateWinRTNew::getNativeService( -- cgit v1.2.3 From 38bb5bae0a05407ec180a140c00231a39ef23cd2 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Wed, 20 Mar 2019 11:07:35 +0100 Subject: Enable bttestui to test QBluetoothSocket::bytesWritten() signal Change-Id: Id29865014043963f7edf22cc81a2bd7a91c55a60 Reviewed-by: Oliver Wolff --- tests/bttestui/btlocaldevice.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/bttestui/btlocaldevice.cpp b/tests/bttestui/btlocaldevice.cpp index fb3a0270..55b99fc3 100644 --- a/tests/bttestui/btlocaldevice.cpp +++ b/tests/bttestui/btlocaldevice.cpp @@ -93,6 +93,9 @@ BtLocalDevice::BtLocalDevice(QObject *parent) : connect(socket, &QBluetoothSocket::connected, this, &BtLocalDevice::socketConnected); connect(socket, &QBluetoothSocket::disconnected, this, &BtLocalDevice::socketDisconnected); connect(socket, &QIODevice::readyRead, this, &BtLocalDevice::readData); + connect(socket, &QBluetoothSocket::bytesWritten, this, [](qint64 bytesWritten){ + qDebug() << "Bytes Written to Client socket:" << bytesWritten; + }); setSecFlags(static_cast(socket->preferredSecurityFlags())); server = new QBluetoothServer(SOCKET_PROTOCOL, this); @@ -682,6 +685,9 @@ void BtLocalDevice::serverNewConnection() connect(client, QOverload::of(&QBluetoothSocket::error), this, &BtLocalDevice::socketError); connect(client, &QBluetoothSocket::connected, this, &BtLocalDevice::socketConnected); + connect(client, &QBluetoothSocket::bytesWritten, this, [](qint64 bytesWritten){ + qDebug() << "Bytes Written to Server socket:" << bytesWritten; + }); serverSockets.append(client); } -- cgit v1.2.3 From 6a8a8f328d1782319b4dc0e4b3497e6613e0f864 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Mon, 1 Apr 2019 13:35:30 +0300 Subject: Add changes file for Qt 5.12.3 + 3f9ba9c07322d90f8f918d9e144e84d422e4cfbd Bluez-DBus: Fix crash at calling disconnect while not connected + a61ee48bc16fae61cabb9a242f3a7c19caa4cbfd LE/Android: add consistency check in method called from Qt API + 16f033fb13a3005eefbe8b7b6ccc52bfda08649e Address compile error on gcc 4.8 + c488aedb42cda76085cfc0ed2602c34c3b5353a6 Bump version + 75648df8fa91fc0918285988e75ec996f0dc0f97 qlowenergycontroller_android_p.h: Remove unrelated preprocessor blocks + 043145014565dad540b416a0b369a7d04630e649 Update SensorTag documentation URL + 545b282d6d4a3c66e00c41ffd83c1a69f6fd0ad2 Ensure DBus based QBluetoothSocket emits bytesWritten() signal + d21da7829811d129b96ca0eb2a56cb6bdae4f1a7 Convert bttestui to Qt5 style connect() syntax Change-Id: Ibbd7fed9375e8990882ab4d85c1875dcdb5b76c4 Reviewed-by: Oliver Wolff --- dist/changes-5.12.3 | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 dist/changes-5.12.3 diff --git a/dist/changes-5.12.3 b/dist/changes-5.12.3 new file mode 100644 index 00000000..2341dae0 --- /dev/null +++ b/dist/changes-5.12.3 @@ -0,0 +1,42 @@ +Qt 5.12.3 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.12.0 through 5.12.2. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +https://doc.qt.io/qt-5/index.html + +The Qt version 5.12 series is binary compatible with the 5.11.x series. +Applications compiled for 5.11 will continue to run with 5.12. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Qt 5.12.3 Changes * +**************************************************************************** + +QtBluetooth +----------- + + - Fixed crash at calling QLowEnergyController::disconnectFromDevice while + not connected. + + - [QTBUG-74513] Fixed missing emission of QBluetoothSocket::bytesWritten() signal on + Bluez v5.46+. + + - Fixed NullPointerException on Android when discovering BTLE services while an + unexpected device connect is triggered. + + - Applied a variety of minor code cleanups. + +QtNfc +----- + + - [QTBUG-74538] Addressed a compile error in qnearfieldtarget_emulator_p.h + when using gcc 4.8. -- cgit v1.2.3 From c161203b5dd91ace4b14d46542c49115e8de8d92 Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Thu, 4 Apr 2019 14:26:04 +0300 Subject: Check the reverted UUID when filtering Change-Id: Idb12d3bb116d6c5b34f1ca145f473b118b58d5ee Reviewed-by: Alex Blasche --- .../qbluetoothservicediscoveryagent_android.cpp | 6 ++- src/bluetooth/qbluetoothsocket_android.cpp | 50 +++++++++++----------- src/bluetooth/qbluetoothsocket_android_p.h | 2 + 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp index ddc53421..e607a27e 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp @@ -47,6 +47,7 @@ #include #include "qbluetoothservicediscoveryagent_p.h" +#include "qbluetoothsocket_android_p.h" #include "android/servicediscoverybroadcastreceiver_p.h" #include "android/localdevicebroadcastreceiver_p.h" @@ -449,8 +450,11 @@ void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QB //Check if the service is in the uuidFilter if (!uuidFilter.isEmpty()) { bool match = uuidFilter.contains(serviceInfo.serviceUuid()); - for (const auto &uuid : qAsConst(uuidFilter)) + match |= uuidFilter.contains(QBluetoothSocketPrivateAndroid::reverseUuid(serviceInfo.serviceUuid())); + for (const auto &uuid : qAsConst(uuidFilter)) { match |= serviceInfo.serviceClassUuids().contains(uuid); + match |= serviceInfo.serviceClassUuids().contains(QBluetoothSocketPrivateAndroid::reverseUuid(uuid)); + } if (!match) continue; diff --git a/src/bluetooth/qbluetoothsocket_android.cpp b/src/bluetooth/qbluetoothsocket_android.cpp index d7f17d17..46bd4a23 100644 --- a/src/bluetooth/qbluetoothsocket_android.cpp +++ b/src/bluetooth/qbluetoothsocket_android.cpp @@ -182,30 +182,6 @@ private: QPointer workerPointer; }; -/* - * This function is part of a workaround for QTBUG-61392 - * - * Returns null uuid if the given \a serviceUuid is not a uuid - * derived from the Bluetooth base uuid. - */ -static QBluetoothUuid reverseUuid(const QBluetoothUuid &serviceUuid) -{ - if (serviceUuid.isNull()) - return QBluetoothUuid(); - - bool isBaseUuid = false; - serviceUuid.toUInt32(&isBaseUuid); - if (isBaseUuid) - return serviceUuid; - - const quint128 original = serviceUuid.toUInt128(); - quint128 reversed; - for (int i = 0; i < 16; i++) - reversed.data[15-i] = original.data[i]; - - return QBluetoothUuid(reversed); -} - QBluetoothSocketPrivateAndroid::QBluetoothSocketPrivateAndroid() : inputThread(0) @@ -942,6 +918,32 @@ qint64 QBluetoothSocketPrivateAndroid::bytesToWrite() const return 0; // nothing because always unbuffered } +/* + * This function is part of a workaround for QTBUG-61392 + * + * Returns null uuid if the given \a serviceUuid is not a uuid + * derived from the Bluetooth base uuid. + */ +QBluetoothUuid QBluetoothSocketPrivateAndroid::reverseUuid(const QBluetoothUuid &serviceUuid) +{ + if (QtAndroid::androidSdkVersion() < 23) + return serviceUuid; + + if (serviceUuid.isNull()) + return QBluetoothUuid(); + + bool isBaseUuid = false; + serviceUuid.toUInt32(&isBaseUuid); + if (isBaseUuid) + return serviceUuid; + + const quint128 original = serviceUuid.toUInt128(); + quint128 reversed; + for (int i = 0; i < 16; i++) + reversed.data[15-i] = original.data[i]; + return QBluetoothUuid{reversed}; +} + bool QBluetoothSocketPrivateAndroid::canReadLine() const { // We cannot access buffer directly as it is part of different thread diff --git a/src/bluetooth/qbluetoothsocket_android_p.h b/src/bluetooth/qbluetoothsocket_android_p.h index 7bf42e32..042e1bcd 100644 --- a/src/bluetooth/qbluetoothsocket_android_p.h +++ b/src/bluetooth/qbluetoothsocket_android_p.h @@ -112,6 +112,8 @@ public: bool canReadLine() const override; qint64 bytesToWrite() const override; + static QBluetoothUuid reverseUuid(const QBluetoothUuid &serviceUuid); + QAndroidJniObject adapter; QAndroidJniObject socketObject; QAndroidJniObject remoteDevice; -- cgit v1.2.3 From f2d0878b82f3ab897bc1ffe9cd377ea8bd796a96 Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Thu, 4 Apr 2019 14:26:23 +0300 Subject: Fix possible leak Change-Id: Ide883a3c354eb42ea0017e1d623843967b522083 Reviewed-by: Alex Blasche --- src/bluetooth/android/servicediscoverybroadcastreceiver.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bluetooth/android/servicediscoverybroadcastreceiver.cpp b/src/bluetooth/android/servicediscoverybroadcastreceiver.cpp index be1953d5..283db623 100644 --- a/src/bluetooth/android/servicediscoverybroadcastreceiver.cpp +++ b/src/bluetooth/android/servicediscoverybroadcastreceiver.cpp @@ -99,7 +99,6 @@ QList ServiceDiscoveryBroadcastReceiver::convertParcelableArray( { QList result; QAndroidJniEnvironment env; - QAndroidJniObject p; jobjectArray parcels = parcelUuidArray.object(); if (!parcels) @@ -107,7 +106,7 @@ QList ServiceDiscoveryBroadcastReceiver::convertParcelableArray( jint size = env->GetArrayLength(parcels); for (int i = 0; i < size; i++) { - p = env->GetObjectArrayElement(parcels, i); + auto p = QAndroidJniObject::fromLocalRef(env->GetObjectArrayElement(parcels, i)); QBluetoothUuid uuid(p.callObjectMethod("toString").toString()); //qCDebug(QT_BT_ANDROID) << uuid.toString(); -- cgit v1.2.3 From a54b8af5c73284119686b762cf4bbbaa2e0dd1cf Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Thu, 4 Apr 2019 14:27:04 +0300 Subject: Use QAndroidJniExceptionCleaner instead of QAndroidJniEnvironment Change-Id: I357091799f04a3ef1c8df78960eb63a46e2b3d6c Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothlocaldevice_android.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/bluetooth/qbluetoothlocaldevice_android.cpp b/src/bluetooth/qbluetoothlocaldevice_android.cpp index b46923eb..40e4c2d4 100644 --- a/src/bluetooth/qbluetoothlocaldevice_android.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_android.cpp @@ -90,23 +90,16 @@ static QAndroidJniObject getDefaultAdapter() QAndroidJniObject adapter = QAndroidJniObject::callStaticObjectMethod( "android/bluetooth/BluetoothAdapter", "getDefaultAdapter", "()Landroid/bluetooth/BluetoothAdapter;"); + QAndroidJniExceptionCleaner exCleaner{QAndroidJniExceptionCleaner::OutputMode::Verbose}; if (!adapter.isValid()) { - QAndroidJniEnvironment env; - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - env->ExceptionClear(); - } + exCleaner.clean(); // workaround stupid bt implementations where first call of BluetoothAdapter.getDefaultAdapter() always fails adapter = QAndroidJniObject::callStaticObjectMethod( "android/bluetooth/BluetoothAdapter", "getDefaultAdapter", "()Landroid/bluetooth/BluetoothAdapter;"); - if (!adapter.isValid()) { - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - env->ExceptionClear(); - } - } + if (!adapter.isValid()) + exCleaner.clean(); } return adapter; } -- cgit v1.2.3 From 6257ca589f435ea9b4f73067098ab54bbc873faf Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Fri, 5 Apr 2019 15:22:08 +0200 Subject: Fix clang-cl errors about narrowing conversions Force value to be a DWORD (unsigned long) instead of HRESULT (signed long). Fixes bluetoothsocket_winrt.cpp(770,10): error: case value evaluates to 2147952460, which cannot be narrowed to type 'long' [-Wc++11-narrowing] Change-Id: I4f1eb75807d4783b835094ad1b33e56f601ffb39 Reviewed-by: Alex Blasche Reviewed-by: Oliver Wolff --- src/bluetooth/qbluetoothsocket_winrt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bluetooth/qbluetoothsocket_winrt.cpp b/src/bluetooth/qbluetoothsocket_winrt.cpp index 79dccdd6..a4af8f72 100644 --- a/src/bluetooth/qbluetoothsocket_winrt.cpp +++ b/src/bluetooth/qbluetoothsocket_winrt.cpp @@ -765,7 +765,7 @@ HRESULT QBluetoothSocketPrivateWinRT::handleConnectOpFinished(ABI::Windows::Foun return S_OK; } - HRESULT hr = action->GetResults(); + DWORD hr = action->GetResults(); switch (hr) { case 0x8007274c: // A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. errorString = QBluetoothSocket::tr("Connection timed out"); -- cgit v1.2.3 From 17e2ac555af234d03144cad72d70a29c62357a38 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Tue, 9 Apr 2019 09:56:54 +0200 Subject: Android: Increase chance of establishing serial connection w/o SPP uuid SDP discovery on Android only returns a flat list of uuids. It mixes service class uuid and service uuids. Furthermore it is unable to detect whether a serial connection uses rfcomm or l2cp as base protocol. The last limitation is the fact that serial sockets can only be established using rfcomm (no l2cp). This poses a serious challenge because there is no way to say whether a given custom service uuid is indeed an SPP based service or whether it uses rfcomm. The only way to know for sure is to connect and check whether it succeeds. This also means QBluetoothServiceInfo instances returned by QBluetoothServiceDiscoveryAgent may or may not mark a serial service with SPP uuid or rfcomm protocol tag. Currently, it guess that that a custom uuid together with SPP uuid implies an rfcomm setup. If the SPP uuid was not found, rfcomm is never set (but is a requirement for QBluetoothSocket). This patch makes QBluetoothSocket on Android a bit more forgiving by assuming every given QBluetoothServiceInfo instance requires rfcomm. After all that's the only supported protocol on Android. Fixes: QTBUG-75035 Change-Id: I498ac5acd2a394b198a113fd23d750bbf17a7f7b Reviewed-by: BogDan Vatra --- src/bluetooth/qbluetoothsocket.cpp | 7 +++++-- src/bluetooth/qbluetoothsocket_android.cpp | 28 +++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/bluetooth/qbluetoothsocket.cpp b/src/bluetooth/qbluetoothsocket.cpp index eecb1401..daa589bb 100644 --- a/src/bluetooth/qbluetoothsocket.cpp +++ b/src/bluetooth/qbluetoothsocket.cpp @@ -360,8 +360,8 @@ qint64 QBluetoothSocket::bytesToWrite() const /*! Attempts to connect to the service described by \a service. - The socket is opened in the given \a openMode. The \l socketType() may change - depending on the protocol required by \a service. + The socket is opened in the given \a openMode. The \l socketType() is ignored + if \a service specifies a differing \l QBluetoothServiceInfo::socketProtocol(). The socket first enters ConnectingState and attempts to connect to the device providing \a service. If a connection is established, QBluetoothSocket enters ConnectedState and @@ -372,6 +372,9 @@ qint64 QBluetoothSocket::bytesToWrite() const Note that most platforms require a pairing prior to connecting to the remote device. Otherwise the connection process may fail. + On Android, only RFCOMM connections are possible. This function ignores any socket protocol indicator + and assumes RFCOMM. + \sa state(), disconnectFromService() */ void QBluetoothSocket::connectToService(const QBluetoothServiceInfo &service, OpenMode openMode) diff --git a/src/bluetooth/qbluetoothsocket_android.cpp b/src/bluetooth/qbluetoothsocket_android.cpp index 46bd4a23..85da325b 100644 --- a/src/bluetooth/qbluetoothsocket_android.cpp +++ b/src/bluetooth/qbluetoothsocket_android.cpp @@ -494,7 +494,33 @@ void QBluetoothSocketPrivateAndroid::connectToService( return; } - if (!ensureNativeSocket(service.socketProtocol())) { + // Workaround for QTBUG-75035 + /* Not all Android devices publish or discover the SPP uuid for serial services. + * Also, Android does not permit the detection of the protocol used by a serial + * Bluetooth connection. + * + * Therefore, QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices() + * may have to guess what protocol a potential custom uuid uses. The guessing works + * reasonably well as long as the SDP discovery finds the SPP uuid. Otherwise + * the SPP and rfcomm protocol info is missing in \a service. + * + * Android only supports RFCOMM (no L2CP). We assume (in favor of user experience) + * that a non-RFCOMM protocol implies a missing SPP uuid during discovery but the user + * still wanting to connect with the given \a service instance. + */ + + auto protocol = service.socketProtocol(); + switch (protocol) { + case QBluetoothServiceInfo::L2capProtocol: + case QBluetoothServiceInfo::UnknownProtocol: + qCWarning(QT_BT_ANDROID) << "Changing socket protocol to RFCOMM"; + protocol = QBluetoothServiceInfo::RfcommProtocol; + break; + case QBluetoothServiceInfo::RfcommProtocol: + break; + } + + if (!ensureNativeSocket(protocol)) { errorString = QBluetoothSocket::tr("Socket type not supported"); q->setSocketError(QBluetoothSocket::UnsupportedProtocolError); return; -- cgit v1.2.3 From 86b4ccddf476f0c18fe04542c52e29b728dbe679 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Fri, 29 Mar 2019 12:03:06 +0100 Subject: winrt: Proper error handling in obtainCharList GetAllCharacteristics was deprecated by MS and GetCharacteristicsAsync should be used. Additionally we should avoid asserts and instead try to handle errors gracefully whenever possible as users will run into "crashes" otherwise Change-Id: I45d52374f8612621b5b3974a973d9a1a95b851ee Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 400 ++++++++++++++--------- src/bluetooth/qlowenergycontroller_winrt_new_p.h | 1 + 2 files changed, 254 insertions(+), 147 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index 42d0a320..6e40e77c 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -78,6 +78,18 @@ typedef ITypedEventHandler Va typedef GattReadClientCharacteristicConfigurationDescriptorResult ClientCharConfigDescriptorResult; typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharConfigDescriptorResult; +#define EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, ret) \ + if (FAILED(hr)) { \ + emitErrorAndQuitThread(hr); \ + ret; \ + } + +#define WARN_AND_CONTINUE_IF_FAILED(hr, msg) \ + if (FAILED(hr)) { \ + qCWarning(QT_BT_WINRT) << msg; \ + continue; \ + } + Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT) QLowEnergyControllerPrivate *createWinRTLowEnergyController() @@ -124,7 +136,7 @@ class QWinRTLowEnergyServiceHandlerNew : public QObject Q_OBJECT public: QWinRTLowEnergyServiceHandlerNew(const QBluetoothUuid &service, - const ComPtr &deviceService) + const ComPtr &deviceService) : mService(service) , mDeviceService(deviceService) { @@ -139,57 +151,46 @@ public slots: void obtainCharList() { mIndicateChars.clear(); - quint16 startHandle = 0; - quint16 endHandle = 0; qCDebug(QT_BT_WINRT) << __FUNCTION__; - ComPtr> characteristics; - HRESULT hr = mDeviceService->GetAllCharacteristics(&characteristics); - Q_ASSERT_SUCCEEDED(hr); - if (!characteristics) { - emit charListObtained(mService, mCharacteristicList, mIndicateChars, startHandle, endHandle); - QThread::currentThread()->quit(); + ComPtr> characteristicsOp; + ComPtr characteristicsResult; + HRESULT hr = mDeviceService->GetCharacteristicsAsync(&characteristicsOp); + EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return); + hr = QWinRTFunctions::await(characteristicsOp, characteristicsResult.GetAddressOf(), + QWinRTFunctions::ProcessMainThreadEvents, 5000); + EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return); + GattCommunicationStatus status; + hr = characteristicsResult->get_Status(&status); + EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return); + if (status != GattCommunicationStatus_Success) { + emitErrorAndQuitThread(QLatin1String("Could not obtain char list")); return; } + ComPtr> characteristics; + hr = characteristicsResult->get_Characteristics(&characteristics); + EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return); uint characteristicsCount; hr = characteristics->get_Size(&characteristicsCount); + EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return); - // If there are no characteristics, we assume that the device is not paired (and not - // discovered by Windows) and we use new API (GetCharacteristicsAsync) to discover them - // without pairing. - if (characteristicsCount == 0) { - ComPtr deviceService3; - hr = mDeviceService.As(&deviceService3); - Q_ASSERT_SUCCEEDED(hr); - ComPtr> asyncResult; - deviceService3->GetCharacteristicsAsync(&asyncResult); - hr = asyncResult->put_Completed( - Callback>( - [this](IAsyncOperation *, AsyncStatus status) { - if (status != AsyncStatus::Completed) { - qCDebug(QT_BT_WINRT) << "Could not obtain characteristics"; - return S_OK; - } - // TODO We should check if we found any characteristics. It makes no sense but - // there is a possibility that device doesn't state any characteristics under a service. - // So, for sanity, we should not continue endless loop here. - obtainCharList(); - return S_OK; - }).Get()); - Q_ASSERT_SUCCEEDED(hr); - return; - } - - Q_ASSERT_SUCCEEDED(hr); mCharacteristicsCountToBeDiscovered = characteristicsCount; for (uint i = 0; i < characteristicsCount; ++i) { ComPtr characteristic; hr = characteristics->GetAt(i, &characteristic); - Q_ASSERT_SUCCEEDED(hr); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain characteristic at" << i; + --mCharacteristicsCountToBeDiscovered; + continue; + } ComPtr characteristic3; hr = characteristic.As(&characteristic3); - Q_ASSERT_SUCCEEDED(hr); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not cast characteristic"; + --mCharacteristicsCountToBeDiscovered; + continue; + } // For some strange reason, Windows doesn't discover descriptors of characteristics (if not paired). // Qt API assumes that all characteristics and their descriptors are discovered in one go. @@ -197,129 +198,195 @@ public slots: // when GetDescriptorsAsync for all characteristics return. ComPtr> descAsyncResult; hr = characteristic3->GetDescriptorsAsync(&descAsyncResult); - Q_ASSERT_SUCCEEDED(hr); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain list of descriptors"; + --mCharacteristicsCountToBeDiscovered; + continue; + } hr = descAsyncResult->put_Completed( - Callback>( - [this, characteristic](IAsyncOperation *op, - AsyncStatus status) { - if (status != AsyncStatus::Completed) { - qCDebug(QT_BT_WINRT) << "Could not obtain descriptors"; + Callback>( + [this, characteristic] + (IAsyncOperation *op, + AsyncStatus status) { + if (status != AsyncStatus::Completed) { + qCWarning(QT_BT_WINRT) << "Descriptor operation unsuccessful"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + quint16 handle; + + HRESULT hr = characteristic->get_AttributeHandle(&handle); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain characteristic's attribute handle"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + QLowEnergyServicePrivate::CharData charData; + charData.valueHandle = handle + 1; + if (mStartHandle == 0 || mStartHandle > handle) + mStartHandle = handle; + if (mEndHandle == 0 || mEndHandle < handle) + mEndHandle = handle; + GUID guuid; + hr = characteristic->get_Uuid(&guuid); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain characteristic's Uuid"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + charData.uuid = QBluetoothUuid(guuid); + GattCharacteristicProperties properties; + hr = characteristic->get_CharacteristicProperties(&properties); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain characteristic's properties"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + charData.properties = QLowEnergyCharacteristic::PropertyTypes(properties & 0xff); + if (charData.properties & QLowEnergyCharacteristic::Read) { + ComPtr> readOp; + hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, + &readOp); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not read characteristic"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); return S_OK; } - quint16 handle; - - HRESULT hr = characteristic->get_AttributeHandle(&handle); - Q_ASSERT_SUCCEEDED(hr); - QLowEnergyServicePrivate::CharData charData; - charData.valueHandle = handle + 1; - if (mStartHandle == 0 || mStartHandle > handle) - mStartHandle = handle; - if (mEndHandle == 0 || mEndHandle < handle) - mEndHandle = handle; - GUID guuid; - hr = characteristic->get_Uuid(&guuid); - Q_ASSERT_SUCCEEDED(hr); - charData.uuid = QBluetoothUuid(guuid); - GattCharacteristicProperties properties; - hr = characteristic->get_CharacteristicProperties(&properties); - Q_ASSERT_SUCCEEDED(hr); - charData.properties = QLowEnergyCharacteristic::PropertyTypes(properties & 0xff); - if (charData.properties & QLowEnergyCharacteristic::Read) { - ComPtr> readOp; - hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, - &readOp); - Q_ASSERT_SUCCEEDED(hr); - ComPtr readResult; - hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); - Q_ASSERT_SUCCEEDED(hr); - if (readResult) - charData.value = byteArrayFromGattResult(readResult); + ComPtr readResult; + hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain characteristic read result"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; } + if (!readResult) + qCWarning(QT_BT_WINRT) << "Characteristic read result is null"; + else + charData.value = byteArrayFromGattResult(readResult); + } + mCharacteristicList.insert(handle, charData); - ComPtr> descriptors; + ComPtr> descriptors; - ComPtr result; - hr = op->GetResults(&result); - Q_ASSERT_SUCCEEDED(hr); - hr = result->get_Descriptors(&descriptors); - Q_ASSERT_SUCCEEDED(hr); + ComPtr result; + hr = op->GetResults(&result); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain descriptor read result"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + GattCommunicationStatus commStatus; + hr = result->get_Status(&commStatus); + if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) { + qCWarning(QT_BT_WINRT) << "Descriptor operation failed"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } - uint descriptorCount; - hr = descriptors->get_Size(&descriptorCount); - Q_ASSERT_SUCCEEDED(hr); - for (uint j = 0; j < descriptorCount; ++j) { - QLowEnergyServicePrivate::DescData descData; - ComPtr descriptor; - hr = descriptors->GetAt(j, &descriptor); - Q_ASSERT_SUCCEEDED(hr); - quint16 descHandle; - hr = descriptor->get_AttributeHandle(&descHandle); - Q_ASSERT_SUCCEEDED(hr); - GUID descriptorUuid; - hr = descriptor->get_Uuid(&descriptorUuid); - Q_ASSERT_SUCCEEDED(hr); - descData.uuid = QBluetoothUuid(descriptorUuid); - if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) { - ComPtr> readOp; - hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp); - Q_ASSERT_SUCCEEDED(hr); - ComPtr readResult; - hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); - Q_ASSERT_SUCCEEDED(hr); - GattClientCharacteristicConfigurationDescriptorValue value; - hr = readResult->get_ClientCharacteristicConfigurationDescriptor(&value); - Q_ASSERT_SUCCEEDED(hr); - quint16 result = 0; - bool correct = false; - if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) { - result |= GattClientCharacteristicConfigurationDescriptorValue_Indicate; - correct = true; - } - if (value & GattClientCharacteristicConfigurationDescriptorValue_Notify) { - result |= GattClientCharacteristicConfigurationDescriptorValue_Notify; - correct = true; - } - if (value == GattClientCharacteristicConfigurationDescriptorValue_None) { - correct = true; - } - if (!correct) - continue; - - descData.value = QByteArray(2, Qt::Uninitialized); - qToLittleEndian(result, descData.value.data()); - mIndicateChars << charData.uuid; - } else { - ComPtr> readOp; - hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, - &readOp); - Q_ASSERT_SUCCEEDED(hr); - ComPtr readResult; - hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); - Q_ASSERT_SUCCEEDED(hr); - if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription) - descData.value = byteArrayFromGattResult(readResult, true); - else - descData.value = byteArrayFromGattResult(readResult); + hr = result->get_Descriptors(&descriptors); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain list of descriptors"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + + uint descriptorCount; + hr = descriptors->get_Size(&descriptorCount); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain list of descriptors' size"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + for (uint j = 0; j < descriptorCount; ++j) { + QLowEnergyServicePrivate::DescData descData; + ComPtr descriptor; + hr = descriptors->GetAt(j, &descriptor); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor") + quint16 descHandle; + hr = descriptor->get_AttributeHandle(&descHandle); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor's attribute handle") + GUID descriptorUuid; + hr = descriptor->get_Uuid(&descriptorUuid); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor's Uuid") + descData.uuid = QBluetoothUuid(descriptorUuid); + charData.descriptorList.insert(descHandle, descData); + if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) { + ComPtr> readOp; + hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not read descriptor value") + ComPtr readResult; + hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not await descriptor read result") + GattClientCharacteristicConfigurationDescriptorValue value; + hr = readResult->get_ClientCharacteristicConfigurationDescriptor(&value); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not get descriptor value from result") + quint16 result = 0; + bool correct = false; + if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) { + result |= GattClientCharacteristicConfigurationDescriptorValue_Indicate; + correct = true; } - charData.descriptorList.insert(descHandle, descData); - } + if (value & GattClientCharacteristicConfigurationDescriptorValue_Notify) { + result |= GattClientCharacteristicConfigurationDescriptorValue_Notify; + correct = true; + } + if (value == GattClientCharacteristicConfigurationDescriptorValue_None) { + correct = true; + } + if (!correct) + continue; - mCharacteristicList.insert(handle, charData); - mCharacteristicsCountToBeDiscovered--; - if (mCharacteristicsCountToBeDiscovered == 0) { - emit charListObtained(mService, mCharacteristicList, mIndicateChars, - mStartHandle, mEndHandle); - QThread::currentThread()->quit(); + descData.value = QByteArray(2, Qt::Uninitialized); + qToLittleEndian(result, descData.value.data()); + mIndicateChars << charData.uuid; + } else { + ComPtr> readOp; + hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, + &readOp); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not read descriptor value") + ComPtr readResult; + hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could await descriptor read result") + if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription) + descData.value = byteArrayFromGattResult(readResult, true); + else + descData.value = byteArrayFromGattResult(readResult); } - return S_OK; - }).Get()); - Q_ASSERT_SUCCEEDED(hr); + charData.descriptorList.insert(descHandle, descData); + } + + mCharacteristicList.insert(handle, charData); + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + }).Get()); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not register descriptor callback"; + --mCharacteristicsCountToBeDiscovered; + continue; + } } + checkAllCharacteristicsDiscovered(); } +private: + bool checkAllCharacteristicsDiscovered(); + void emitErrorAndQuitThread(HRESULT hr); + void emitErrorAndQuitThread(const QString &error); + public: QBluetoothUuid mService; - ComPtr mDeviceService; + ComPtr mDeviceService; QHash mCharacteristicList; uint mCharacteristicsCountToBeDiscovered; quint16 mStartHandle = 0; @@ -331,8 +398,32 @@ signals: QLowEnergyServicePrivate::CharData> charList, QVector indicateChars, QLowEnergyHandle startHandle, QLowEnergyHandle endHandle); + void errorOccured(const QString &error); }; +bool QWinRTLowEnergyServiceHandlerNew::checkAllCharacteristicsDiscovered() +{ + if (mCharacteristicsCountToBeDiscovered == 0) { + emit charListObtained(mService, mCharacteristicList, mIndicateChars, + mStartHandle, mEndHandle); + QThread::currentThread()->quit(); + return true; + } + + return false; +} + +void QWinRTLowEnergyServiceHandlerNew::emitErrorAndQuitThread(HRESULT hr) +{ + emitErrorAndQuitThread(qt_error_string(hr)); +} + +void QWinRTLowEnergyServiceHandlerNew::emitErrorAndQuitThread(const QString &error) +{ + emit errorOccured(error); + QThread::currentThread()->quit(); +} + QLowEnergyControllerPrivateWinRTNew::QLowEnergyControllerPrivateWinRTNew() : QLowEnergyControllerPrivate() { @@ -737,6 +828,9 @@ void QLowEnergyControllerPrivateWinRTNew::discoverServiceDetails(const QBluetoot ComPtr deviceService2; HRESULT hr = deviceService.As(&deviceService2); Q_ASSERT_SUCCEEDED(hr); + ComPtr deviceService3; + hr = deviceService.As(&deviceService3); + RETURN_IF_FAILED("Could not cast device service", return); ComPtr> deviceServices; hr = deviceService2->GetAllIncludedServices(&deviceServices); if (FAILED(hr)) { // ERROR_ACCESS_DISABLED_BY_POLICY @@ -771,12 +865,14 @@ void QLowEnergyControllerPrivateWinRTNew::discoverServiceDetails(const QBluetoot } QWinRTLowEnergyServiceHandlerNew *worker - = new QWinRTLowEnergyServiceHandlerNew(service, deviceService2); + = new QWinRTLowEnergyServiceHandlerNew(service, deviceService3); QThread *thread = new QThread; worker->moveToThread(thread); connect(thread, &QThread::started, worker, &QWinRTLowEnergyServiceHandlerNew::obtainCharList); connect(thread, &QThread::finished, thread, &QObject::deleteLater); connect(thread, &QThread::finished, worker, &QObject::deleteLater); + connect(worker, &QWinRTLowEnergyServiceHandlerNew::errorOccured, + this, &QLowEnergyControllerPrivateWinRTNew::handleServiceHandlerError); connect(worker, &QWinRTLowEnergyServiceHandlerNew::charListObtained, [this, thread](const QBluetoothUuid &service, QHash charList, QVector indicateChars, @@ -1317,6 +1413,16 @@ void QLowEnergyControllerPrivateWinRTNew::characteristicChanged( emit service->characteristicChanged(characteristic, data); } +void QLowEnergyControllerPrivateWinRTNew::handleServiceHandlerError(const QString &error) +{ + if (state != QLowEnergyController::DiscoveringState) + return; + + qCWarning(QT_BT_WINRT) << "Error while discovering services:" << error; + setState(QLowEnergyController::UnconnectedState); + setError(QLowEnergyController::ConnectionError); +} + QT_END_NAMESPACE #include "qlowenergycontroller_winrt_new.moc" diff --git a/src/bluetooth/qlowenergycontroller_winrt_new_p.h b/src/bluetooth/qlowenergycontroller_winrt_new_p.h index b3990acd..93b763a4 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new_p.h +++ b/src/bluetooth/qlowenergycontroller_winrt_new_p.h @@ -118,6 +118,7 @@ public: private slots: void characteristicChanged(quint16 charHandle, const QByteArray &data); + void handleServiceHandlerError(const QString &error); private: Microsoft::WRL::ComPtr mDevice; -- cgit v1.2.3 From da71597d2c440528de2c84606d553d0ef7e922ae Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Fri, 29 Mar 2019 13:54:17 +0100 Subject: winrt: Do not assert/crash in registerforValueChanges/unregisterFromValueChanges Change-Id: I6ea4ae73b53cd705156e15ce8820467bddedcf6f Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index 6e40e77c..de86be06 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -664,6 +664,12 @@ void QLowEnergyControllerPrivateWinRTNew::registerForValueChanges(const QBluetoo return; } ComPtr characteristic = getNativeCharacteristic(serviceUuid, charUuid); + if (!characteristic) { + qCDebug(QT_BT_WINRT).nospace() << "Could not obtain native characteristic " << charUuid + << " from service " << serviceUuid << ". Qt will not be able to signal" + << " changes for this characteristic."; + return; + } EventRegistrationToken token; HRESULT hr; @@ -691,8 +697,14 @@ void QLowEnergyControllerPrivateWinRTNew::unregisterFromValueChanges() qCDebug(QT_BT_WINRT) << "Unregistering " << mValueChangedTokens.count() << " value change tokens"; HRESULT hr; for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens)) { + if (!entry.characteristic) { + qCWarning(QT_BT_WINRT) << "Unregistering from value changes for characteristic failed." + << "Characteristic has been deleted"; + continue; + } hr = entry.characteristic->remove_ValueChanged(entry.token); - Q_ASSERT_SUCCEEDED(hr); + if (FAILED(hr)) + qCWarning(QT_BT_WINRT) << "Unregistering from value changes for characteristic failed."; } mValueChangedTokens.clear(); } -- cgit v1.2.3 From d4c0af715be750ddfc28daadff19617e80af6406 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Fri, 29 Mar 2019 13:53:15 +0100 Subject: winrt: Avoid asserts in connectToDevice get_GattServices is marked deprecated and GetGattServicesAsync should be used instead. We should avoid asserts whenever possible as users might run into "crashes" otherwise. Change-Id: Ibd07a846f3a23d3390061e42fd034d21ec2b9901 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 208 ++++++++++++----------- 1 file changed, 106 insertions(+), 102 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index de86be06..bc578502 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -90,6 +90,14 @@ typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharCo continue; \ } +#define CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, msg, ret) \ + if (FAILED(hr)) { \ + qCWarning(QT_BT_WINRT) << msg; \ + setError(QLowEnergyController::ConnectionError); \ + setState(QLowEnergyController::UnconnectedState); \ + ret; \ + } + Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT) QLowEnergyControllerPrivate *createWinRTLowEnergyController() @@ -458,21 +466,21 @@ void QLowEnergyControllerPrivateWinRTNew::connectToDevice() HRESULT hr = GetActivationFactory( HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice).Get(), &deviceStatics); - Q_ASSERT_SUCCEEDED(hr); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain device factory", return) ComPtr> deviceFromIdOperation; hr = deviceStatics->FromBluetoothAddressAsync(remoteDevice.toUInt64(), &deviceFromIdOperation); - Q_ASSERT_SUCCEEDED(hr); - hr = QWinRTFunctions::await(deviceFromIdOperation, mDevice.GetAddressOf()); - Q_ASSERT_SUCCEEDED(hr); - - if (!mDevice) { - qCDebug(QT_BT_WINRT) << "Could not find LE device"; + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not find LE device from address", return) + hr = QWinRTFunctions::await(deviceFromIdOperation, mDevice.GetAddressOf(), + QWinRTFunctions::ProcessMainThreadEvents, 5000); + if (FAILED(hr) || !mDevice) { + qCWarning(QT_BT_WINRT) << "Could not find LE device"; setError(QLowEnergyController::InvalidBluetoothAdapterError); setState(QLowEnergyController::UnconnectedState); + return; } BluetoothConnectionStatus status; hr = mDevice->get_ConnectionStatus(&status); - Q_ASSERT_SUCCEEDED(hr); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain device's connection status", return) hr = QEventDispatcherWinRT::runOnXamlThread([this, q]() { HRESULT hr; hr = mDevice->add_ConnectionStatusChanged( @@ -480,7 +488,7 @@ void QLowEnergyControllerPrivateWinRTNew::connectToDevice() BluetoothConnectionStatus status; HRESULT hr; hr = dev->get_ConnectionStatus(&status); - Q_ASSERT_SUCCEEDED(hr); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain connection status", return S_OK) if (state == QLowEnergyController::ConnectingState && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) { setState(QLowEnergyController::ConnectedState); @@ -489,16 +497,17 @@ void QLowEnergyControllerPrivateWinRTNew::connectToDevice() && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) { invalidateServices(); unregisterFromValueChanges(); + mDevice = nullptr; setError(QLowEnergyController::RemoteHostClosedError); setState(QLowEnergyController::UnconnectedState); emit q->disconnected(); } return S_OK; }).Get(), &mStatusChangedToken); - Q_ASSERT_SUCCEEDED(hr); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not register connection status callback", return S_OK) return S_OK; }); - Q_ASSERT_SUCCEEDED(hr); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not add status callback on Xaml thread", return) if (status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) { setState(QLowEnergyController::ConnectedState); @@ -506,104 +515,99 @@ void QLowEnergyControllerPrivateWinRTNew::connectToDevice() return; } + ComPtr device3; + hr = mDevice.As(&device3); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast device", return) + ComPtr> deviceServicesOp; + hr = device3->GetGattServicesAsync(&deviceServicesOp); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain services", return) + ComPtr deviceServicesResult; + hr = QWinRTFunctions::await(deviceServicesOp, deviceServicesResult.GetAddressOf(), + QWinRTFunctions::ProcessMainThreadEvents, 5000); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await services operation", return) + GattCommunicationStatus commStatus; + hr = deviceServicesResult->get_Status(&commStatus); + if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) { + qCWarning(QT_BT_WINRT()) << "Service operation failed"; + setError(QLowEnergyController::ConnectionError); + setState(QLowEnergyController::UnconnectedState); + return; + } + ComPtr> deviceServices; - hr = mDevice->get_GattServices(&deviceServices); - Q_ASSERT_SUCCEEDED(hr); + hr = deviceServicesResult->get_Services(&deviceServices); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain list of services", return) uint serviceCount; hr = deviceServices->get_Size(&serviceCount); - Q_ASSERT_SUCCEEDED(hr); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service count", return) - // Windows doesn't provide any explicit connect/reconnect. We need to 'start using' the device - // and windows will initiate connection as a cause of that. - if (serviceCount == 0) { - // If we don't have any services discovered yet (for devices not paired), the simplest - // way to initiate connect is to start discovering services. It's not exactly how Qt API - // expects it to be but IMHO doesn't do any harm either. Services will already be discovered - // when coonnection state changes to 'connected'. - ComPtr device3; - hr = mDevice.As(&device3); - Q_ASSERT_SUCCEEDED(hr); - ComPtr> asyncResult; - hr = device3->GetGattServicesAsync(&asyncResult); - Q_ASSERT_SUCCEEDED(hr); - hr = asyncResult->put_Completed( - Callback>( - [this, q](IAsyncOperation *, AsyncStatus status) { - if (status != AsyncStatus::Completed) { - qCDebug(QT_BT_WINRT) << "Could not obtain services"; - return S_OK; - } - setState(QLowEnergyController::ConnectedState); - emit q->connected(); - return S_OK; - }).Get()); - Q_ASSERT_SUCCEEDED(hr); - } else { - // Windows Phone automatically connects to the device as soon as a service value is read/written. - // Thus we read one value in order to establish the connection. - for (uint i = 0; i < serviceCount; ++i) { - ComPtr service; - hr = deviceServices->GetAt(i, &service); - Q_ASSERT_SUCCEEDED(hr); - ComPtr service2; - hr = service.As(&service2); - Q_ASSERT_SUCCEEDED(hr); - ComPtr> characteristics; - hr = service2->GetAllCharacteristics(&characteristics); - if (hr == E_ACCESSDENIED) { - // Everything will work as expected up until this point if the manifest capabilties - // for bluetooth LE are not set. - qCWarning(QT_BT_WINRT) << "Could not obtain characteristic list. Please check your " - "manifest capabilities"; - setState(QLowEnergyController::UnconnectedState); - setError(QLowEnergyController::ConnectionError); - return; - } else if (FAILED(hr)) { - qCWarning(QT_BT_WINRT) << "Connecting to device failed: " - << qt_error_string(hr); - setError(QLowEnergyController::ConnectionError); - setState(QLowEnergyController::UnconnectedState); - return; - } - uint characteristicsCount; - hr = characteristics->get_Size(&characteristicsCount); - Q_ASSERT_SUCCEEDED(hr); - for (uint j = 0; j < characteristicsCount; ++j) { - ComPtr characteristic; - hr = characteristics->GetAt(j, &characteristic); - Q_ASSERT_SUCCEEDED(hr); - ComPtr> op; - GattCharacteristicProperties props; - hr = characteristic->get_CharacteristicProperties(&props); - Q_ASSERT_SUCCEEDED(hr); - if (!(props & GattCharacteristicProperties_Read)) - continue; - hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode::BluetoothCacheMode_Uncached, &op); - Q_ASSERT_SUCCEEDED(hr); - ComPtr result; - hr = QWinRTFunctions::await(op, result.GetAddressOf()); - if (hr == E_INVALIDARG) { - // E_INVALIDARG happens when user tries to connect to a device that was paired - // before but is not available. - qCDebug(QT_BT_WINRT) << "Could not obtain characteristic read result that triggers" - "device connection. Is the device reachable?"; - setError(QLowEnergyController::ConnectionError); - setState(QLowEnergyController::UnconnectedState); - return; - } - Q_ASSERT_SUCCEEDED(hr); - ComPtr buffer; - hr = result->get_Value(&buffer); - Q_ASSERT_SUCCEEDED(hr); - if (!buffer) { - qCDebug(QT_BT_WINRT) << "Problem reading value"; - setError(QLowEnergyController::ConnectionError); - setState(QLowEnergyController::UnconnectedState); - } - return; + // Windows automatically connects to the device as soon as a service value is read/written. + // Thus we read one value in order to establish the connection. + for (uint i = 0; i < serviceCount; ++i) { + ComPtr service; + hr = deviceServices->GetAt(i, &service); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service"); + ComPtr service3; + hr = service.As(&service3); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not cast service"); + ComPtr> characteristicsOp; + hr = service3->GetCharacteristicsAsync(&characteristicsOp); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic"); + ComPtr characteristicsResult; + hr = QWinRTFunctions::await(characteristicsOp, characteristicsResult.GetAddressOf(), + QWinRTFunctions::ProcessMainThreadEvents, 5000); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not await characteristic operation"); + GattCommunicationStatus commStatus; + hr = characteristicsResult->get_Status(&commStatus); + if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) { + qCWarning(QT_BT_WINRT) << "Characteristic operation failed"; + continue; + } + ComPtr> characteristics; + hr = characteristicsResult->get_Characteristics(&characteristics); + if (hr == E_ACCESSDENIED) { + // Everything will work as expected up until this point if the manifest capabilties + // for bluetooth LE are not set. + qCWarning(QT_BT_WINRT) << "Could not obtain characteristic list. Please check your " + "manifest capabilities"; + setState(QLowEnergyController::UnconnectedState); + setError(QLowEnergyController::ConnectionError); + return; + } + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic list"); + uint characteristicsCount; + hr = characteristics->get_Size(&characteristicsCount); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic list's size"); + for (uint j = 0; j < characteristicsCount; ++j) { + ComPtr characteristic; + hr = characteristics->GetAt(j, &characteristic); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic"); + ComPtr> op; + GattCharacteristicProperties props; + hr = characteristic->get_CharacteristicProperties(&props); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic's properties"); + if (!(props & GattCharacteristicProperties_Read)) + continue; + hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode::BluetoothCacheMode_Uncached, &op); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not read characteristic value"); + ComPtr result; + hr = QWinRTFunctions::await(op, result.GetAddressOf()); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could await characteristic read"); + ComPtr buffer; + hr = result->get_Value(&buffer); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic value"); + if (!buffer) { + qCDebug(QT_BT_WINRT) << "Problem reading value"; + continue; } + return; } } + + qCWarning(QT_BT_WINRT) << "Could not obtain characteristic read result that triggers" + "device connection. Is the device reachable?"; + setError(QLowEnergyController::ConnectionError); + setState(QLowEnergyController::UnconnectedState); } void QLowEnergyControllerPrivateWinRTNew::disconnectFromDevice() -- cgit v1.2.3 From cddb1c96c6b5d3f2e5e3715bfda6721fb31410c6 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Mon, 1 Apr 2019 08:51:54 +0200 Subject: winrt: unregister status change callbacks on disconnect We have to prevent late callbacks. Change-Id: Iedb36cdfb9708403bdbd68b68718c4b26f38d657 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 28 +++++++++++++++--------- src/bluetooth/qlowenergycontroller_winrt_new_p.h | 2 ++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index bc578502..9c543b6d 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -93,6 +93,7 @@ typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharCo #define CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, msg, ret) \ if (FAILED(hr)) { \ qCWarning(QT_BT_WINRT) << msg; \ + unregisterFromStatusChanges(); \ setError(QLowEnergyController::ConnectionError); \ setState(QLowEnergyController::UnconnectedState); \ ret; \ @@ -440,9 +441,7 @@ QLowEnergyControllerPrivateWinRTNew::QLowEnergyControllerPrivateWinRTNew() QLowEnergyControllerPrivateWinRTNew::~QLowEnergyControllerPrivateWinRTNew() { - if (mDevice && mStatusChangedToken.value) - mDevice->remove_ConnectionStatusChanged(mStatusChangedToken); - + unregisterFromStatusChanges(); unregisterFromValueChanges(); } @@ -497,6 +496,7 @@ void QLowEnergyControllerPrivateWinRTNew::connectToDevice() && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) { invalidateServices(); unregisterFromValueChanges(); + unregisterFromStatusChanges(); mDevice = nullptr; setError(QLowEnergyController::RemoteHostClosedError); setState(QLowEnergyController::UnconnectedState); @@ -525,12 +525,14 @@ void QLowEnergyControllerPrivateWinRTNew::connectToDevice() hr = QWinRTFunctions::await(deviceServicesOp, deviceServicesResult.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000); CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await services operation", return) + GattCommunicationStatus commStatus; hr = deviceServicesResult->get_Status(&commStatus); if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) { qCWarning(QT_BT_WINRT()) << "Service operation failed"; setError(QLowEnergyController::ConnectionError); setState(QLowEnergyController::UnconnectedState); + unregisterFromStatusChanges(); return; } @@ -572,6 +574,7 @@ void QLowEnergyControllerPrivateWinRTNew::connectToDevice() "manifest capabilities"; setState(QLowEnergyController::UnconnectedState); setError(QLowEnergyController::ConnectionError); + unregisterFromStatusChanges(); return; } WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic list"); @@ -606,6 +609,7 @@ void QLowEnergyControllerPrivateWinRTNew::connectToDevice() qCWarning(QT_BT_WINRT) << "Could not obtain characteristic read result that triggers" "device connection. Is the device reachable?"; + unregisterFromStatusChanges(); setError(QLowEnergyController::ConnectionError); setState(QLowEnergyController::UnconnectedState); } @@ -616,13 +620,8 @@ void QLowEnergyControllerPrivateWinRTNew::disconnectFromDevice() Q_Q(QLowEnergyController); setState(QLowEnergyController::ClosingState); unregisterFromValueChanges(); - if (mDevice) { - if (mStatusChangedToken.value) { - mDevice->remove_ConnectionStatusChanged(mStatusChangedToken); - mStatusChangedToken.value = 0; - } - mDevice = nullptr; - } + unregisterFromStatusChanges(); + mDevice = nullptr; setState(QLowEnergyController::UnconnectedState); emit q->disconnected(); } @@ -713,6 +712,15 @@ void QLowEnergyControllerPrivateWinRTNew::unregisterFromValueChanges() mValueChangedTokens.clear(); } +void QLowEnergyControllerPrivateWinRTNew::unregisterFromStatusChanges() +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__; + if (mDevice && mStatusChangedToken.value) { + mDevice->remove_ConnectionStatusChanged(mStatusChangedToken); + mStatusChangedToken.value = 0; + } +} + void QLowEnergyControllerPrivateWinRTNew::obtainIncludedServices( QSharedPointer servicePointer, ComPtr service) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new_p.h b/src/bluetooth/qlowenergycontroller_winrt_new_p.h index 93b763a4..bf409745 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new_p.h +++ b/src/bluetooth/qlowenergycontroller_winrt_new_p.h @@ -143,6 +143,8 @@ private: void registerForValueChanges(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid); void unregisterFromValueChanges(); + void unregisterFromStatusChanges(); + void obtainIncludedServices(QSharedPointer servicePointer, Microsoft::WRL::ComPtr nativeService); -- cgit v1.2.3 From 8b643c80a81f4897e0a1ec6442820c60bbc802b1 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Tue, 9 Apr 2019 10:39:43 +0200 Subject: Streamline QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices The changes are of cosmetic or code optimization nature. The functionality is not changed at all. Change-Id: Ideb63d87ed07201024b73e2f40d33393b68963bc Reviewed-by: Timur Pocheptsov Reviewed-by: Oliver Wolff --- .../qbluetoothservicediscoveryagent_android.cpp | 65 +++++++++++----------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp index e607a27e..15afe76c 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp @@ -336,25 +336,24 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_processFetchedUuids( void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QBluetoothDeviceInfo &remoteDevice, const QList &uuids) { - /* Android doesn't provide decent SDP data. A list of uuids is close to meaning-less + /* Android doesn't provide decent SDP data. A flat list of uuids is all we get. * * The following approach is chosen: * - If we see an SPP service class and we see * one or more custom uuids we match them up. Such services will always be SPP services. * - If we see a custom uuid but no SPP uuid then we return - * BluetoothServiceInfo instance with just a servuceUuid (no service class set) + * BluetoothServiceInfo instance with just a serviceUuid (no service class set) * - Any other service uuid will stand on its own. * */ Q_Q(QBluetoothServiceDiscoveryAgent); //find SPP and custom uuid - QBluetoothUuid uuid; int sppIndex = -1; QVector customUuids; for (int i = 0; i < uuids.count(); i++) { - uuid = uuids.at(i); + const QBluetoothUuid uuid = uuids.at(i); if (uuid.isNull()) continue; @@ -370,12 +369,29 @@ void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QB customUuids.append(i); } + auto rfcommProtocolDescriptorList = []() -> QBluetoothServiceInfo::Sequence { + QBluetoothServiceInfo::Sequence protocol; + protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm)) + << QVariant::fromValue(0); + return protocol; + }; + + auto sppProfileDescriptorList = []() -> QBluetoothServiceInfo::Sequence { + QBluetoothServiceInfo::Sequence profileSequence; + QBluetoothServiceInfo::Sequence classId; + classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort)); + classId << QVariant::fromValue(quint16(0x100)); + profileSequence.append(QVariant::fromValue(classId)); + return profileSequence; + }; + for (int i = 0; i < uuids.count(); i++) { if (i == sppIndex && !customUuids.isEmpty()) continue; QBluetoothServiceInfo serviceInfo; serviceInfo.setDevice(remoteDevice); + const QBluetoothUuid &uuid = uuids.at(i); QBluetoothServiceInfo::Sequence protocolDescriptorList; { @@ -388,48 +404,34 @@ void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QB //we have a custom uuid of service class type SPP //set rfcomm protocol - QBluetoothServiceInfo::Sequence protocol; - protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm)) - << QVariant::fromValue(0); - protocolDescriptorList.append(QVariant::fromValue(protocol)); + protocolDescriptorList.append(QVariant::fromValue(rfcommProtocolDescriptorList())); - QBluetoothServiceInfo::Sequence profileSequence; - QBluetoothServiceInfo::Sequence classId; - classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort)); - classId << QVariant::fromValue(quint16(0x100)); - profileSequence.append(QVariant::fromValue(classId)); + //set SPP profile descriptor list serviceInfo.setAttribute(QBluetoothServiceInfo::BluetoothProfileDescriptorList, - profileSequence); + sppProfileDescriptorList()); - classId.clear(); + QBluetoothServiceInfo::Sequence classId; //set SPP service class uuid - classId << QVariant::fromValue(uuids.at(i)); + classId << QVariant::fromValue(uuid); classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort)); serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId); serviceInfo.setServiceName(QBluetoothServiceDiscoveryAgent::tr("Serial Port Profile")); - serviceInfo.setServiceUuid(uuids.at(i)); + serviceInfo.setServiceUuid(uuid); } else if (sppIndex == i && customUuids.isEmpty()) { //set rfcomm protocol - QBluetoothServiceInfo::Sequence protocol; - protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm)) - << QVariant::fromValue(0); - protocolDescriptorList.append(QVariant::fromValue(protocol)); + protocolDescriptorList.append(QVariant::fromValue(rfcommProtocolDescriptorList())); - QBluetoothServiceInfo::Sequence profileSequence; - QBluetoothServiceInfo::Sequence classId; - classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort)); - classId << QVariant::fromValue(quint16(0x100)); - profileSequence.append(QVariant::fromValue(classId)); + //set SPP profile descriptor list serviceInfo.setAttribute(QBluetoothServiceInfo::BluetoothProfileDescriptorList, - profileSequence); + sppProfileDescriptorList()); //also we need to set the custom uuid to the SPP uuid //otherwise QBluetoothSocket::connectToService() would fail due to a missing service uuid - serviceInfo.setServiceUuid(uuids.at(i)); + serviceInfo.setServiceUuid(uuid); } else if (customUuids.contains(i)) { //custom uuid but no serial port - serviceInfo.setServiceUuid(uuids.at(i)); + serviceInfo.setServiceUuid(uuid); } serviceInfo.setAttribute(QBluetoothServiceInfo::ProtocolDescriptorList, protocolDescriptorList); @@ -440,10 +442,9 @@ void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QB if (!customUuids.contains(i)) { //if we don't have custom uuid use it as class id as well QBluetoothServiceInfo::Sequence classId; - classId << QVariant::fromValue(uuids.at(i)); + classId << QVariant::fromValue(uuid); serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId); - QBluetoothUuid::ServiceClassUuid clsId - = static_cast(uuids.at(i).toUInt16()); + auto clsId = QBluetoothUuid::ServiceClassUuid(uuid.toUInt16()); serviceInfo.setServiceName(QBluetoothUuid::serviceClassToString(clsId)); } -- cgit v1.2.3 From a1f63352338ba8bd6b212a393f9f45e0c817008c Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Fri, 29 Mar 2019 13:53:40 +0100 Subject: winrt: Avoid asserts in getNativeCharacteristic Change-Id: I4a555222071c75a56b45477b1a894b929ffc1e58 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 25 ++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index 9c543b6d..a6514188 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -644,9 +644,30 @@ ComPtr QLowEnergyControllerPrivateWinRTNew::getNativeCharac if (!service) return nullptr; - ComPtr> characteristics; - HRESULT hr = service->GetCharacteristics(charUuid, &characteristics); + ComPtr service3; + HRESULT hr = service.As(&service3); + RETURN_IF_FAILED("Could not cast service", return nullptr); + + ComPtr> op; + ComPtr result; + hr = service3->GetCharacteristicsForUuidAsync(charUuid, &op); RETURN_IF_FAILED("Could not obtain native characteristics for service", return nullptr); + hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000); + RETURN_IF_FAILED("Could not await completion of characteristic operation", return nullptr); + GattCommunicationStatus status; + hr = result->get_Status(&status); + if (FAILED(hr) || status != GattCommunicationStatus_Success) { + qErrnoWarning(hr, "Native characteristic operation failed."); + return nullptr; + } + ComPtr> characteristics; + hr = result->get_Characteristics(&characteristics); + RETURN_IF_FAILED("Could not obtain characteristic list.", return nullptr); + uint size; + hr = characteristics->get_Size(&size); + RETURN_IF_FAILED("Could not obtain characteristic list's size.", return nullptr); + if (size != 1) + qErrnoWarning("More than 1 characteristic found."); ComPtr characteristic; hr = characteristics->GetAt(0, &characteristic); RETURN_IF_FAILED("Could not obtain first characteristic for service", return nullptr); -- cgit v1.2.3 From edece2d218cfc63185b08d21f500bf705c71b227 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Fri, 29 Mar 2019 13:54:48 +0100 Subject: winrt: Avoid deprecated functions and asserts in obtainIncludedServices GetAllIncludedServices is marked deprecated and GetIncludedServicesAsync should be used instead. Change-Id: Ib5778809c1f90be9cddc9dd12e831c998f50469c Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 29 ++++++++++++++++-------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index a6514188..2e7a8074 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -747,25 +747,36 @@ void QLowEnergyControllerPrivateWinRTNew::obtainIncludedServices( ComPtr service) { Q_Q(QLowEnergyController); - ComPtr service2; - HRESULT hr = service.As(&service2); - Q_ASSERT_SUCCEEDED(hr); - ComPtr> includedServices; - hr = service2->GetAllIncludedServices(&includedServices); + ComPtr service3; + HRESULT hr = service.As(&service3); + RETURN_IF_FAILED("Could not cast service", return); + ComPtr> op; + hr = service3->GetIncludedServicesAsync(&op); // Some devices return ERROR_ACCESS_DISABLED_BY_POLICY - if (FAILED(hr)) + RETURN_IF_FAILED("Could not obtain included services", return); + ComPtr result; + hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000); + RETURN_IF_FAILED("Could not await service operation", return); + GattCommunicationStatus status; + hr = result->get_Status(&status); + if (FAILED(hr) || status != GattCommunicationStatus_Success) { + qErrnoWarning("Could not obtain list of included services"); return; + } + ComPtr> includedServices; + hr = result->get_Services(&includedServices); + RETURN_IF_FAILED("Could not obtain service list", return); uint count; hr = includedServices->get_Size(&count); - Q_ASSERT_SUCCEEDED(hr); + RETURN_IF_FAILED("Could not obtain service list's size", return); for (uint i = 0; i < count; ++i) { ComPtr includedService; hr = includedServices->GetAt(i, &includedService); - Q_ASSERT_SUCCEEDED(hr); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service from list"); GUID guuid; hr = includedService->get_Uuid(&guuid); - Q_ASSERT_SUCCEEDED(hr); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain included service's Uuid"); const QBluetoothUuid includedUuid(guuid); QSharedPointer includedPointer; if (serviceList.contains(includedUuid)) { -- cgit v1.2.3 From 0ee482ecf5fd1d74852541bd1b3e22d616551e51 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Fri, 29 Mar 2019 13:55:02 +0100 Subject: winrt: Avoid deprecated functions and asserts in discoverServices get_GattServices is deprecated and we should use the operation's result to obtain the list of services. Change-Id: I45e7217f0667ade4fe975e13010238dbe48f197e Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 37 +++++++++++++++++------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index 2e7a8074..55ba6aaa 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -806,31 +806,46 @@ void QLowEnergyControllerPrivateWinRTNew::discoverServices() ComPtr device3; HRESULT hr = mDevice.As(&device3); - Q_ASSERT_SUCCEEDED(hr); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast device", return); ComPtr> asyncResult; hr = device3->GetGattServicesAsync(&asyncResult); - Q_ASSERT_SUCCEEDED(hr); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain services", return); hr = QEventDispatcherWinRT::runOnXamlThread( [asyncResult, q, this] () { HRESULT hr = asyncResult->put_Completed( Callback>( - [this, q](IAsyncOperation *, AsyncStatus status) { + [this, q](IAsyncOperation *op, + AsyncStatus status) { if (status != AsyncStatus::Completed) { qCDebug(QT_BT_WINRT) << "Could not obtain services"; return S_OK; } + ComPtr result; ComPtr> deviceServices; - HRESULT hr = mDevice->get_GattServices(&deviceServices); - Q_ASSERT_SUCCEEDED(hr); + HRESULT hr = op->GetResults(&result); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service discovery result", + return S_OK); + GattCommunicationStatus commStatus; + hr = result->get_Status(&commStatus); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service discovery status", + return S_OK); + if (commStatus != GattCommunicationStatus_Success) + return S_OK; + + hr = result->get_Services(&deviceServices); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service list", + return S_OK); + uint serviceCount; hr = deviceServices->get_Size(&serviceCount); - Q_ASSERT_SUCCEEDED(hr); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service list size", + return S_OK); for (uint i = 0; i < serviceCount; ++i) { ComPtr deviceService; hr = deviceServices->GetAt(i, &deviceService); - Q_ASSERT_SUCCEEDED(hr); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service"); GUID guuid; hr = deviceService->get_Uuid(&guuid); - Q_ASSERT_SUCCEEDED(hr); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service's Uuid"); const QBluetoothUuid service(guuid); QSharedPointer pointer; @@ -856,10 +871,12 @@ void QLowEnergyControllerPrivateWinRTNew::discoverServices() return S_OK; }).Get()); - Q_ASSERT_SUCCEEDED(hr); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not register service discovery callback", + return S_OK) return hr; }); - Q_ASSERT_SUCCEEDED(hr); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not run registration in Xaml thread", + return) } void QLowEnergyControllerPrivateWinRTNew::discoverServiceDetails(const QBluetoothUuid &service) -- cgit v1.2.3 From 30e04016cf8ab757d8cb89ee8b0adfa137915bb8 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Fri, 29 Mar 2019 13:55:44 +0100 Subject: winrt: Avoid asserts in readDescriptor Change-Id: I0a446e5bc81e3d306da7c15e0c1907dab459a129 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 96 ++++++++++++++++++------ 1 file changed, 71 insertions(+), 25 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index 55ba6aaa..728d992f 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -99,6 +99,13 @@ typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharCo ret; \ } +#define CHECK_HR_AND_SET_SERVICE_ERROR(hr, msg, service, error, ret) \ + if (FAILED(hr)) { \ + qCDebug(QT_BT_WINRT) << msg; \ + service->setError(error); \ + ret; \ + } + Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT) QLowEnergyControllerPrivate *createWinRTLowEnergyController() @@ -1082,7 +1089,7 @@ void QLowEnergyControllerPrivateWinRTNew::readDescriptor( HRESULT hr; hr = QEventDispatcherWinRT::runOnXamlThread([charHandle, descHandle, service, this]() { - QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle); + const QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle); ComPtr characteristic = getNativeCharacteristic(service->uuid, charData.uuid); if (!characteristic) { qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid @@ -1094,12 +1101,14 @@ void QLowEnergyControllerPrivateWinRTNew::readDescriptor( // Get native descriptor if (!charData.descriptorList.contains(descHandle)) qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "cannot be found in characteristic" << charHandle; - QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle); - if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) { + const QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle); + const QBluetoothUuid descUuid = descData.uuid; + if (descUuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) { ComPtr> readOp; HRESULT hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp); - Q_ASSERT_SUCCEEDED(hr); - auto readCompletedLambda = [&charData, charHandle, &descData, descHandle, service] + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not read client characteristic configuration", + service, QLowEnergyService::DescriptorReadError, return S_OK) + auto readCompletedLambda = [charHandle, descHandle, service] (IAsyncOperation *op, AsyncStatus status) { if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { @@ -1110,18 +1119,12 @@ void QLowEnergyControllerPrivateWinRTNew::readDescriptor( ComPtr iValue; HRESULT hr; hr = op->GetResults(&iValue); - if (FAILED(hr)) { - qCDebug(QT_BT_WINRT) << "Could not obtain result for descriptor" << descHandle; - service->setError(QLowEnergyService::DescriptorReadError); - return S_OK; - } + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for descriptor", + service, QLowEnergyService::DescriptorReadError, return S_OK) GattClientCharacteristicConfigurationDescriptorValue value; hr = iValue->get_ClientCharacteristicConfigurationDescriptor(&value); - if (FAILED(hr)) { - qCDebug(QT_BT_WINRT) << "Could not obtain value for descriptor" << descHandle; - service->setError(QLowEnergyService::DescriptorReadError); - return S_OK; - } + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain value for descriptor", + service, QLowEnergyService::DescriptorReadError, return S_OK) quint16 result = 0; bool correct = false; if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) { @@ -1140,9 +1143,11 @@ void QLowEnergyControllerPrivateWinRTNew::readDescriptor( service->setError(QLowEnergyService::DescriptorReadError); return S_OK; } + QLowEnergyServicePrivate::DescData descData; + descData.uuid = QBluetoothUuid::ClientCharacteristicConfiguration; descData.value = QByteArray(2, Qt::Uninitialized); qToLittleEndian(result, descData.value.data()); - charData.descriptorList.insert(descHandle, descData); + service->characteristicList[charHandle].descriptorList[descHandle] = descData; emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle), descData.value); return S_OK; @@ -1150,19 +1155,56 @@ void QLowEnergyControllerPrivateWinRTNew::readDescriptor( hr = readOp->put_Completed( Callback>( readCompletedLambda).Get()); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor read callback", + service, QLowEnergyService::DescriptorReadError, return S_OK) return S_OK; } else { + ComPtr characteristic3; + HRESULT hr = characteristic.As(&characteristic3); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast characteristic", + service, QLowEnergyService::DescriptorReadError, return S_OK) + ComPtr> op; + hr = characteristic3->GetDescriptorsForUuidAsync(descData.uuid, &op); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor for uuid", + service, QLowEnergyService::DescriptorReadError, return S_OK) + ComPtr result; + hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await descritpor read result", + service, QLowEnergyService::DescriptorReadError, return S_OK) + + GattCommunicationStatus commStatus; + hr = result->get_Status(&commStatus); + if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) { + qErrnoWarning("Could not obtain list of descriptors"); + service->setError(QLowEnergyService::DescriptorReadError); + return S_OK; + } + ComPtr> descriptors; - HRESULT hr = characteristic->GetDescriptors(descData.uuid, &descriptors); - Q_ASSERT_SUCCEEDED(hr); + hr = result->get_Descriptors(&descriptors); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor list", + service, QLowEnergyService::DescriptorReadError, return S_OK) + uint size; + hr = descriptors->get_Size(&size); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await descritpor list's size", + service, QLowEnergyService::DescriptorReadError, return S_OK) + if (size == 0) { + qCWarning(QT_BT_WINRT) << "No descriptor with uuid" << descData.uuid << "was found."; + service->setError(QLowEnergyService::DescriptorReadError); + return S_OK; + } else if (size > 1) { + qCWarning(QT_BT_WINRT) << "There is more than 1 descriptor with uuid" << descData.uuid; + } + ComPtr descriptor; hr = descriptors->GetAt(0, &descriptor); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descritpor from list", + service, QLowEnergyService::DescriptorReadError, return S_OK) ComPtr> readOp; hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp); - Q_ASSERT_SUCCEEDED(hr); - auto readCompletedLambda = [&charData, charHandle, &descData, descHandle, service] + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not read descriptor value", + service, QLowEnergyService::DescriptorReadError, return S_OK) + auto readCompletedLambda = [charHandle, descHandle, descUuid, service] (IAsyncOperation *op, AsyncStatus status) { if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { @@ -1178,22 +1220,26 @@ void QLowEnergyControllerPrivateWinRTNew::readDescriptor( service->setError(QLowEnergyService::DescriptorReadError); return S_OK; } + QLowEnergyServicePrivate::DescData descData; + descData.uuid = descUuid; if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription) descData.value = byteArrayFromGattResult(descriptorValue, true); else descData.value = byteArrayFromGattResult(descriptorValue); - charData.descriptorList.insert(descHandle, descData); + service->characteristicList[charHandle].descriptorList[descHandle] = descData; emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle), descData.value); return S_OK; }; hr = readOp->put_Completed(Callback>( readCompletedLambda).Get()); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor read callback", + service, QLowEnergyService::DescriptorReadError, return S_OK) return S_OK; } }); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not run registration on Xaml thread", + service, QLowEnergyService::DescriptorReadError, return) } void QLowEnergyControllerPrivateWinRTNew::writeCharacteristic( -- cgit v1.2.3 From c558fdbf15aace0cf09c031f1e417329b9aa6afa Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Fri, 29 Mar 2019 13:55:51 +0100 Subject: winrt: Avoid asserts in writeDescriptor Change-Id: Ib8de4e76893104f27d79f44440a0186985d9abe5 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 83 +++++++++++++++++------- 1 file changed, 59 insertions(+), 24 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index 728d992f..398e167f 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -1404,7 +1404,8 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor( } ComPtr> writeOp; HRESULT hr = characteristic->WriteClientCharacteristicConfigurationDescriptorAsync(value, &writeOp); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not write client characteristic configuration", + service, QLowEnergyService::DescriptorWriteError, return S_OK) auto writeCompletedLambda = [charHandle, descHandle, newValue, service, this] (IAsyncOperation *op, AsyncStatus status) { @@ -1416,13 +1417,10 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor( GattCommunicationStatus result; HRESULT hr; hr = op->GetResults(&result); - if (FAILED(hr)) { - qCDebug(QT_BT_WINRT) << "Could not obtain result for descriptor" << descHandle; - service->setError(QLowEnergyService::DescriptorWriteError); - return S_OK; - } + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for descriptor", + service, QLowEnergyService::DescriptorWriteError, return S_OK) if (result != GattCommunicationStatus_Success) { - qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed"; + qCWarning(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed"; service->setError(QLowEnergyService::DescriptorWriteError); return S_OK; } @@ -1434,35 +1432,73 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor( hr = writeOp->put_Completed( Callback>( writeCompletedLambda).Get()); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor write callback", + service, QLowEnergyService::DescriptorWriteError, return S_OK) } else { + ComPtr characteristic3; + HRESULT hr = characteristic.As(&characteristic3); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast characteristic", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + ComPtr> op; + hr = characteristic3->GetDescriptorsForUuidAsync(descData.uuid, &op); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor from Uuid", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + ComPtr result; + hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await descriptor operation", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + GattCommunicationStatus commStatus; + hr = result->get_Status(&commStatus); + if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) { + qCWarning(QT_BT_WINRT) << "Descriptor operation failed"; + service->setError(QLowEnergyService::DescriptorWriteError); + return S_OK; + } ComPtr> descriptors; - HRESULT hr = characteristic->GetDescriptors(descData.uuid, &descriptors); - Q_ASSERT_SUCCEEDED(hr); + hr = result->get_Descriptors(&descriptors); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain list of descriptors", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + uint size; + hr = descriptors->get_Size(&size); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain list of descriptors' size", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + if (size == 0) { + qCWarning(QT_BT_WINRT) << "No descriptor with uuid" << descData.uuid << "was found."; + return S_OK; + } else if (size > 1) { + qCWarning(QT_BT_WINRT) << "There is more than 1 descriptor with uuid" << descData.uuid; + } ComPtr descriptor; hr = descriptors->GetAt(0, &descriptor); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor", + service, QLowEnergyService::DescriptorWriteError, return S_OK) ComPtr bufferFactory; hr = GetActivationFactory( HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), &bufferFactory); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain buffer factory", + service, QLowEnergyService::DescriptorWriteError, return S_OK) ComPtr buffer; const quint32 length = quint32(newValue.length()); hr = bufferFactory->Create(length, &buffer); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not create buffer", + service, QLowEnergyService::DescriptorWriteError, return S_OK) hr = buffer->put_Length(length); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer length", + service, QLowEnergyService::DescriptorWriteError, return S_OK) ComPtr byteAccess; hr = buffer.As(&byteAccess); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast buffer", + service, QLowEnergyService::DescriptorWriteError, return S_OK) byte *bytes; hr = byteAccess->Buffer(&bytes); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer", + service, QLowEnergyService::DescriptorWriteError, return S_OK) memcpy(bytes, newValue, length); ComPtr> writeOp; hr = descriptor->WriteValueAsync(buffer.Get(), &writeOp); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not write descriptor value", + service, QLowEnergyService::DescriptorWriteError, return S_OK) auto writeCompletedLambda = [charHandle, descHandle, newValue, service, this] (IAsyncOperation *op, AsyncStatus status) { @@ -1474,11 +1510,8 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor( GattCommunicationStatus result; HRESULT hr; hr = op->GetResults(&result); - if (FAILED(hr)) { - qCDebug(QT_BT_WINRT) << "Could not obtain result for descriptor" << descHandle; - service->setError(QLowEnergyService::DescriptorWriteError); - return S_OK; - } + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for descriptor", + service, QLowEnergyService::DescriptorWriteError, return S_OK) if (result != GattCommunicationStatus_Success) { qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed"; service->setError(QLowEnergyService::DescriptorWriteError); @@ -1492,12 +1525,14 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor( hr = writeOp->put_Completed( Callback>( writeCompletedLambda).Get()); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor write callback", + service, QLowEnergyService::DescriptorWriteError, return S_OK) return S_OK; } return S_OK; }); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not run registration on Xaml thread", + service, QLowEnergyService::DescriptorWriteError, return) } -- cgit v1.2.3 From 6e8f174ea1183c2e3c9bbb61c54b0aa8ad8c3b9c Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Wed, 3 Apr 2019 09:47:49 +0200 Subject: winrt: Avoid asserts in readCharacteristic Change-Id: I3617e22fcded5dc7066bf05c6d7bf791fce4dd02 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index 398e167f..fb8c3108 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -1034,7 +1034,8 @@ void QLowEnergyControllerPrivateWinRTNew::readCharacteristic( } ComPtr> readOp; HRESULT hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not read characteristic", + service, QLowEnergyService::CharacteristicReadError, return S_OK) auto readCompletedLambda = [charData, charHandle, service] (IAsyncOperation *op, AsyncStatus status) { @@ -1046,11 +1047,8 @@ void QLowEnergyControllerPrivateWinRTNew::readCharacteristic( ComPtr characteristicValue; HRESULT hr; hr = op->GetResults(&characteristicValue); - if (FAILED(hr)) { - qCDebug(QT_BT_WINRT) << "Could not obtain result for characteristic" << charHandle; - service->setError(QLowEnergyService::CharacteristicReadError); - return S_OK; - } + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for characteristic", + service, QLowEnergyService::CharacteristicReadError, return S_OK) const QByteArray value = byteArrayFromGattResult(characteristicValue); QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle); @@ -1061,10 +1059,12 @@ void QLowEnergyControllerPrivateWinRTNew::readCharacteristic( }; hr = readOp->put_Completed(Callback>( readCompletedLambda).Get()); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register characteristic read callback", + service, QLowEnergyService::CharacteristicReadError, return S_OK) return S_OK; }); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not run registration on Xaml thread", + service, QLowEnergyService::CharacteristicReadError, return) } void QLowEnergyControllerPrivateWinRTNew::readDescriptor( -- cgit v1.2.3 From a087a33773b3573ab099deb476f528699481f553 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Wed, 3 Apr 2019 09:51:20 +0200 Subject: winrt: Avoid asserts in writeCharacteristic Change-Id: I3044324fb300b918ae466d3c4430c4dd55eed2bd Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 27 ++++++++++++++++-------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index fb8c3108..25ce081f 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -1283,25 +1283,31 @@ void QLowEnergyControllerPrivateWinRTNew::writeCharacteristic( HRESULT hr = GetActivationFactory( HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), &bufferFactory); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain buffer factory", + service, QLowEnergyService::CharacteristicWriteError, return S_OK) ComPtr buffer; const quint32 length = quint32(newValue.length()); hr = bufferFactory->Create(length, &buffer); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not create buffer", + service, QLowEnergyService::CharacteristicWriteError, return S_OK) hr = buffer->put_Length(length); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer length", + service, QLowEnergyService::CharacteristicWriteError, return S_OK) ComPtr byteAccess; hr = buffer.As(&byteAccess); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast buffer", + service, QLowEnergyService::CharacteristicWriteError, return S_OK) byte *bytes; hr = byteAccess->Buffer(&bytes); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer", + service, QLowEnergyService::CharacteristicWriteError, return S_OK) memcpy(bytes, newValue, length); ComPtr> writeOp; GattWriteOption option = writeWithResponse ? GattWriteOption_WriteWithResponse : GattWriteOption_WriteWithoutResponse; hr = characteristic->WriteValueWithOptionAsync(buffer.Get(), option, &writeOp); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could write characteristic", + service, QLowEnergyService::CharacteristicWriteError, return S_OK) auto writeCompletedLambda =[charData, charHandle, newValue, service, writeWithResponse, this] (IAsyncOperation *op, AsyncStatus status) { @@ -1319,7 +1325,8 @@ void QLowEnergyControllerPrivateWinRTNew::writeCharacteristic( service->setError(QLowEnergyService::CharacteristicWriteError); return S_OK; } - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain characteristic write result", + service, QLowEnergyService::CharacteristicWriteError, return S_OK) if (result != GattCommunicationStatus_Success) { qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "write operation failed"; service->setError(QLowEnergyService::CharacteristicWriteError); @@ -1337,10 +1344,12 @@ void QLowEnergyControllerPrivateWinRTNew::writeCharacteristic( hr = writeOp->put_Completed( Callback>( writeCompletedLambda).Get()); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register characteristic write callback", + service, QLowEnergyService::CharacteristicWriteError, return S_OK) return S_OK; }); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not run registration on Xaml thread", + service, QLowEnergyService::CharacteristicWriteError, return) } void QLowEnergyControllerPrivateWinRTNew::writeDescriptor( -- cgit v1.2.3 From 1fdbe1c6a9b818b5928537f176fcf026b31e17a2 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Wed, 3 Apr 2019 10:14:01 +0200 Subject: winrt: Avoid asserts in discoverServiceDetails Change-Id: Ib949c16d80a09c47a0bb332e026459438c901031 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 41 +++++++++++++++--------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index 25ce081f..5a33e51b 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -905,35 +905,45 @@ void QLowEnergyControllerPrivateWinRTNew::discoverServiceDetails(const QBluetoot QSharedPointer pointer = serviceList.value(service); pointer->setState(QLowEnergyService::DiscoveringServices); - ComPtr deviceService2; - HRESULT hr = deviceService.As(&deviceService2); - Q_ASSERT_SUCCEEDED(hr); ComPtr deviceService3; - hr = deviceService.As(&deviceService3); - RETURN_IF_FAILED("Could not cast device service", return); - ComPtr> deviceServices; - hr = deviceService2->GetAllIncludedServices(&deviceServices); - if (FAILED(hr)) { // ERROR_ACCESS_DISABLED_BY_POLICY - qCDebug(QT_BT_WINRT) << "Could not obtain included services list for" << service; + HRESULT hr = deviceService.As(&deviceService3); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast service", + pointer, QLowEnergyService::UnknownError, return) + ComPtr> op; + hr = deviceService3->GetIncludedServicesAsync(&op); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain included service list", + pointer, QLowEnergyService::UnknownError, return) + ComPtr result; + hr = QWinRTFunctions::await(op, result.GetAddressOf()); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await service operation", + pointer, QLowEnergyService::UnknownError, return) + GattCommunicationStatus status; + hr = result->get_Status(&status); + if (FAILED(hr) || status != GattCommunicationStatus_Success) { + qCDebug(QT_BT_WINRT) << "Obtaining list of included services failed"; pointer->setError(QLowEnergyService::UnknownError); - pointer->setState(QLowEnergyService::InvalidService); return; } + ComPtr> deviceServices; + hr = result->get_Services(&deviceServices); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain service list from result", + pointer, QLowEnergyService::UnknownError, return) uint serviceCount; hr = deviceServices->get_Size(&serviceCount); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain included service list's size", + pointer, QLowEnergyService::UnknownError, return) for (uint i = 0; i < serviceCount; ++i) { ComPtr includedService; hr = deviceServices->GetAt(i, &includedService); - Q_ASSERT_SUCCEEDED(hr); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service from list") GUID guuid; hr = includedService->get_Uuid(&guuid); - Q_ASSERT_SUCCEEDED(hr); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service Uuid") const QBluetoothUuid service(guuid); if (service.isNull()) { qCDebug(QT_BT_WINRT) << "Could not find service"; - return; + continue; } pointer->includedServices.append(service); @@ -974,7 +984,8 @@ void QLowEnergyControllerPrivateWinRTNew::discoverServiceDetails(const QBluetoot registerForValueChanges(service, indicateChar); return S_OK; }); - Q_ASSERT_SUCCEEDED(hr); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register for value changes in Xaml thread", + pointer, QLowEnergyService::UnknownError, return) pointer->setState(QLowEnergyService::ServiceDiscovered); thread->exit(0); -- cgit v1.2.3 From 04fc614b5650f39331ed672c9f11f57c1df0836c Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Tue, 9 Apr 2019 15:51:18 +0200 Subject: Android: Don't filter SPP uuid Even though it is not recommended to listen on reserverd BT UUIDs, there are SPP services which do not advertise with a custom uuid or the custom uuid is not an SPP service. In such cases we want a BluetoothServiceInfo instance that advertises SPP as serviceUuid(). Change-Id: Ic54d663392f8f8b2ba5684c57216bf2b69aca477 Reviewed-by: BogDan Vatra --- .../qbluetoothservicediscoveryagent_android.cpp | 23 +++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp index 15afe76c..5e6ddadb 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp @@ -340,16 +340,21 @@ void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QB * * The following approach is chosen: * - If we see an SPP service class and we see - * one or more custom uuids we match them up. Such services will always be SPP services. + * one or more custom uuids we match them up. Such services will always + * be SPP services. There is the chance that a custom uuid is eronously + * mapped as being an SPP service. In addition, the SPP uuid will be mapped as + * standalone SPP service. * - If we see a custom uuid but no SPP uuid then we return - * BluetoothServiceInfo instance with just a serviceUuid (no service class set) + * BluetoothServiceInfo instance with just a serviceUuid (no service class set) + * - If we don't find any custom uuid but the SPP uuid, we return a + * BluetoothServiceInfo instance where classId and serviceUuid() are set to SPP. * - Any other service uuid will stand on its own. * */ Q_Q(QBluetoothServiceDiscoveryAgent); //find SPP and custom uuid - int sppIndex = -1; + bool haveSppClass = false; QVector customUuids; for (int i = 0; i < uuids.count(); i++) { @@ -360,9 +365,8 @@ void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QB //check for SPP protocol bool ok = false; - quint16 uuid16 = uuid.toUInt16(&ok); - if (ok && uuid16 == QBluetoothUuid::SerialPort) - sppIndex = i; + auto uuid16 = uuid.toUInt16(&ok); + haveSppClass |= ok && uuid16 == QBluetoothUuid::SerialPort; //check for custom uuid if (uuid.minimumSize() == 16) @@ -386,9 +390,6 @@ void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QB }; for (int i = 0; i < uuids.count(); i++) { - if (i == sppIndex && !customUuids.isEmpty()) - continue; - QBluetoothServiceInfo serviceInfo; serviceInfo.setDevice(remoteDevice); const QBluetoothUuid &uuid = uuids.at(i); @@ -400,7 +401,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QB protocolDescriptorList.append(QVariant::fromValue(protocol)); } - if (customUuids.contains(i) && sppIndex > -1) { + if (customUuids.contains(i) && haveSppClass) { //we have a custom uuid of service class type SPP //set rfcomm protocol @@ -418,7 +419,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QB serviceInfo.setServiceName(QBluetoothServiceDiscoveryAgent::tr("Serial Port Profile")); serviceInfo.setServiceUuid(uuid); - } else if (sppIndex == i && customUuids.isEmpty()) { + } else if (uuid == QBluetoothUuid{QBluetoothUuid::SerialPort}) { //set rfcomm protocol protocolDescriptorList.append(QVariant::fromValue(rfcommProtocolDescriptorList())); -- cgit v1.2.3 From 72f3ad1dbe66ad4700417e6f681f92320e2fd183 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Tue, 9 Apr 2019 15:57:23 +0200 Subject: Don't create QBluetoothServiceInfo when uuid is null Sometimes Android returns a null uuid as SDP result. There is no point processing them further. Change-Id: I07b52e79a31becda72452e3446aca9ea4933968b Reviewed-by: Timur Pocheptsov --- src/bluetooth/qbluetoothservicediscoveryagent_android.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp index 5e6ddadb..3ab0d580 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp @@ -390,9 +390,12 @@ void QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices(const QB }; for (int i = 0; i < uuids.count(); i++) { + const QBluetoothUuid &uuid = uuids.at(i); + if (uuid.isNull()) + continue; + QBluetoothServiceInfo serviceInfo; serviceInfo.setDevice(remoteDevice); - const QBluetoothUuid &uuid = uuids.at(i); QBluetoothServiceInfo::Sequence protocolDescriptorList; { -- cgit v1.2.3 From 320a6b28499010b8eed6d507da5ebc23b56f5ccc Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Sun, 24 Feb 2019 10:55:05 +0100 Subject: Update plugins.qmltypes for Qt 5.13 Change-Id: I518201f08040260e85968347cc4c68330f1b8787 Reviewed-by: Alex Blasche --- src/imports/bluetooth/plugins.qmltypes | 4 ++-- src/imports/nfc/plugins.qmltypes | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/imports/bluetooth/plugins.qmltypes b/src/imports/bluetooth/plugins.qmltypes index 163d8413..9318fe93 100644 --- a/src/imports/bluetooth/plugins.qmltypes +++ b/src/imports/bluetooth/plugins.qmltypes @@ -4,10 +4,10 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable QtBluetooth 5.12' +// 'qmlplugindump -nonrelocatable QtBluetooth 5.13' Module { - dependencies: ["QtQuick 2.12"] + dependencies: ["QtQuick 2.0"] Component { name: "QDeclarativeBluetoothDiscoveryModel" prototype: "QAbstractListModel" diff --git a/src/imports/nfc/plugins.qmltypes b/src/imports/nfc/plugins.qmltypes index 81ce21d0..d99cac23 100644 --- a/src/imports/nfc/plugins.qmltypes +++ b/src/imports/nfc/plugins.qmltypes @@ -4,10 +4,10 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable QtNfc 5.12' +// 'qmlplugindump -nonrelocatable QtNfc 5.13' Module { - dependencies: ["QtQuick 2.12"] + dependencies: ["QtQuick 2.0"] Component { name: "QDeclarativeNdefFilter" prototype: "QObject" -- cgit v1.2.3 From 6aade96108f48d20382950aff5610e0df24e5616 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Wed, 10 Apr 2019 15:06:30 +0200 Subject: winrt: Fix reading of descriptor values We have to access the service data instead of relying on passed references as these references might have run out of scope and thus might not be valid any more. Fixes: QTBUG-75070 Change-Id: I02ad0fef2337488c926fb950ddf2da6eda56a396 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt.cpp b/src/bluetooth/qlowenergycontroller_winrt.cpp index 6efa2231..f946b541 100644 --- a/src/bluetooth/qlowenergycontroller_winrt.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt.cpp @@ -779,7 +779,7 @@ void QLowEnergyControllerPrivateWinRT::readDescriptor(const QSharedPointercharacteristicList.value(charHandle); + const QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle); ComPtr characteristic = getNativeCharacteristic(service->uuid, charData.uuid); if (!characteristic) { qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid @@ -791,12 +791,13 @@ void QLowEnergyControllerPrivateWinRT::readDescriptor(const QSharedPointer> readOp; HRESULT hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp); Q_ASSERT_SUCCEEDED(hr); - auto readCompletedLambda = [&charData, charHandle, &descData, descHandle, service] + auto readCompletedLambda = [charHandle, descHandle, service] (IAsyncOperation *op, AsyncStatus status) { if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { @@ -837,9 +838,11 @@ void QLowEnergyControllerPrivateWinRT::readDescriptor(const QSharedPointersetError(QLowEnergyService::DescriptorReadError); return S_OK; } + QLowEnergyServicePrivate::DescData descData; + descData.uuid = QBluetoothUuid::ClientCharacteristicConfiguration; descData.value = QByteArray(2, Qt::Uninitialized); qToLittleEndian(result, descData.value.data()); - charData.descriptorList.insert(descHandle, descData); + service->characteristicList[charHandle].descriptorList[descHandle] = descData; emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle), descData.value); return S_OK; @@ -857,7 +860,7 @@ void QLowEnergyControllerPrivateWinRT::readDescriptor(const QSharedPointer> readOp; hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp); Q_ASSERT_SUCCEEDED(hr); - auto readCompletedLambda = [&charData, charHandle, &descData, descHandle, service] + auto readCompletedLambda = [charHandle, descHandle, descUuid, service] (IAsyncOperation *op, AsyncStatus status) { if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { @@ -873,11 +876,12 @@ void QLowEnergyControllerPrivateWinRT::readDescriptor(const QSharedPointersetError(QLowEnergyService::DescriptorReadError); return S_OK; } - if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription) + QLowEnergyServicePrivate::DescData descData; + if (descUuid == QBluetoothUuid::CharacteristicUserDescription) descData.value = byteArrayFromGattResult(descriptorValue, true); else descData.value = byteArrayFromGattResult(descriptorValue); - charData.descriptorList.insert(descHandle, descData); + service->characteristicList[charHandle].descriptorList[descHandle] = descData; emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle), descData.value); return S_OK; -- cgit v1.2.3 From 1bf25e130d4d6ae859c4f2aabe2a752532aee5e5 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Mon, 1 Apr 2019 10:00:52 +0200 Subject: winrt: Avoid asserts in helper functions Change-Id: I1759d7507d778ee60c6727621a3f58a7c7509718 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 32 ++++++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index 5a33e51b..5501c4f3 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -121,15 +121,28 @@ QLowEnergyControllerPrivate *createWinRTLowEnergyController() static QByteArray byteArrayFromBuffer(const ComPtr &buffer, bool isWCharString = false) { + if (!buffer) { + qCWarning(QT_BT_WINRT) << "nullptr passed to byteArrayFromBuffer"; + return QByteArray(); + } ComPtr byteAccess; HRESULT hr = buffer.As(&byteAccess); - Q_ASSERT_SUCCEEDED(hr); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not cast Buffer to ByteAccess"; + return QByteArray(); + } char *data; hr = byteAccess->Buffer(reinterpret_cast(&data)); - Q_ASSERT_SUCCEEDED(hr); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain buffer data"; + return QByteArray(); + } UINT32 size; hr = buffer->get_Length(&size); - Q_ASSERT_SUCCEEDED(hr); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain buffer length"; + return QByteArray(); + } if (isWCharString) { QString valueString = QString::fromUtf16(reinterpret_cast(data)).left(size / 2); return valueString.toUtf8(); @@ -143,7 +156,10 @@ static QByteArray byteArrayFromGattResult(const ComPtr &gattRes ComPtr buffer; HRESULT hr; hr = gattResult->get_Value(&buffer); - Q_ASSERT_SUCCEEDED(hr); + if (FAILED(hr) || !buffer) { + qCWarning(QT_BT_WINRT) << "Could not obtain buffer from GattReadResult"; + return QByteArray(); + } return byteArrayFromBuffer(buffer, isWCharString); } @@ -690,7 +706,7 @@ void QLowEnergyControllerPrivateWinRTNew::registerForValueChanges(const QBluetoo GUID guuid; HRESULT hr; hr = entry.characteristic->get_Uuid(&guuid); - Q_ASSERT_SUCCEEDED(hr); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic's Uuid") if (QBluetoothUuid(guuid) == charUuid) return; } @@ -710,14 +726,14 @@ void QLowEnergyControllerPrivateWinRTNew::registerForValueChanges(const QBluetoo HRESULT hr; quint16 handle; hr = characteristic->get_AttributeHandle(&handle); - Q_ASSERT_SUCCEEDED(hr); + RETURN_IF_FAILED("Could not obtain characteristic's handle", return S_OK) ComPtr buffer; hr = args->get_CharacteristicValue(&buffer); - Q_ASSERT_SUCCEEDED(hr); + RETURN_IF_FAILED("Could not obtain characteristic's value", return S_OK) characteristicChanged(handle, byteArrayFromBuffer(buffer)); return S_OK; }).Get(), &token); - Q_ASSERT_SUCCEEDED(hr); + RETURN_IF_FAILED("Could not register characteristic for value changes", return) mValueChangedTokens.append(ValueChangedEntry(characteristic, token)); qCDebug(QT_BT_WINRT) << "Characteristic" << charUuid << "in service" << serviceUuid << "registered for value changes"; -- cgit v1.2.3 From 25d556c2097445d16cc05b3961f86f1b6ae60ed1 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Wed, 3 Apr 2019 13:34:20 +0200 Subject: winrt: Move byteArrayFromBuffer into qbluetoothutils_winrt That code has been (unneededly) duplicated several times. We should have that helper function just once. Change-Id: I28fc9c5f7f7218b7870dc30bec228c9af8c6b090 Reviewed-by: Alex Blasche --- .../qbluetoothdevicediscoveryagent_winrt.cpp | 15 ---------- .../qbluetoothservicediscoveryagent_winrt.cpp | 18 ------------ src/bluetooth/qbluetoothutils_winrt.cpp | 25 +++++++++++++++++ src/bluetooth/qbluetoothutils_winrt_p.h | 16 +++++++++++ src/bluetooth/qlowenergycontroller_winrt.cpp | 19 +------------ src/bluetooth/qlowenergycontroller_winrt_new.cpp | 32 +--------------------- 6 files changed, 43 insertions(+), 82 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp index e0209693..177b8082 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp @@ -87,21 +87,6 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT) continue; \ } -static QByteArray byteArrayFromBuffer(const ComPtr &buffer) -{ - ComPtr byteAccess; - HRESULT hr = buffer.As(&byteAccess); - Q_ASSERT_SUCCEEDED(hr); - char *data; - hr = byteAccess->Buffer(reinterpret_cast(&data)); - Q_ASSERT_SUCCEEDED(hr); - UINT32 size; - hr = buffer->get_Length(&size); - Q_ASSERT_SUCCEEDED(hr); - return QByteArray(data, int(size)); -} - - static ManufacturerData extractManufacturerData(ComPtr ad) { ManufacturerData ret; diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp index c6b00346..9f82a202 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp @@ -81,24 +81,6 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT) #define TYPE_STRING 37 #define TYPE_SEQUENCE 53 -static QByteArray byteArrayFromBuffer(const ComPtr &buffer, bool isWCharString = false) -{ - ComPtr byteAccess; - HRESULT hr = buffer.As(&byteAccess); - Q_ASSERT_SUCCEEDED(hr); - char *data; - hr = byteAccess->Buffer(reinterpret_cast(&data)); - Q_ASSERT_SUCCEEDED(hr); - UINT32 size; - hr = buffer->get_Length(&size); - Q_ASSERT_SUCCEEDED(hr); - if (isWCharString) { - QString valueString = QString::fromUtf16(reinterpret_cast(data)).left(size / 2); - return valueString.toUtf8(); - } - return QByteArray(data, size); -} - class QWinRTBluetoothServiceDiscoveryWorker : public QObject { Q_OBJECT diff --git a/src/bluetooth/qbluetoothutils_winrt.cpp b/src/bluetooth/qbluetoothutils_winrt.cpp index 1d44221b..de4355c6 100644 --- a/src/bluetooth/qbluetoothutils_winrt.cpp +++ b/src/bluetooth/qbluetoothutils_winrt.cpp @@ -42,12 +42,15 @@ #include +#include #include #include +#include using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; using namespace ABI::Windows::Foundation::Metadata; +using namespace ABI::Windows::Storage::Streams; QT_BEGIN_NAMESPACE @@ -76,4 +79,26 @@ bool supportsNewLEApi() return apiPresent; } +QByteArray byteArrayFromBuffer(const ComPtr &buffer, bool isWCharString) +{ + if (!buffer) { + qErrnoWarning("nullptr passed to byteArrayFromBuffer"); + return QByteArray(); + } + ComPtr byteAccess; + HRESULT hr = buffer.As(&byteAccess); + RETURN_IF_FAILED("Could not cast buffer", return QByteArray()) + char *data; + hr = byteAccess->Buffer(reinterpret_cast(&data)); + RETURN_IF_FAILED("Could not obtain buffer data", return QByteArray()) + UINT32 size; + hr = buffer->get_Length(&size); + RETURN_IF_FAILED("Could not obtain buffer size", return QByteArray()) + if (isWCharString) { + QString valueString = QString::fromUtf16(reinterpret_cast(data)).left(size / 2); + return valueString.toUtf8(); + } + return QByteArray(data, qint32(size)); +} + QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothutils_winrt_p.h b/src/bluetooth/qbluetoothutils_winrt_p.h index c272bae1..93950fc9 100644 --- a/src/bluetooth/qbluetoothutils_winrt_p.h +++ b/src/bluetooth/qbluetoothutils_winrt_p.h @@ -53,10 +53,26 @@ #include +#include + +namespace ABI { + namespace Windows { + namespace Storage { + namespace Streams { + struct IBuffer; + } + } + } +} + QT_BEGIN_NAMESPACE bool supportsNewLEApi(); +using NativeBuffer = ABI::Windows::Storage::Streams::IBuffer; +QByteArray byteArrayFromBuffer(const Microsoft::WRL::ComPtr &buffer, + bool isWCharString = false); + QT_END_NAMESPACE #endif // QBLUETOOTHSOCKET_WINRT_P_H diff --git a/src/bluetooth/qlowenergycontroller_winrt.cpp b/src/bluetooth/qlowenergycontroller_winrt.cpp index 989c5443..a9eb523b 100644 --- a/src/bluetooth/qlowenergycontroller_winrt.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qlowenergycontroller_winrt_p.h" +#include "qbluetoothutils_winrt_p.h" #include #include @@ -104,24 +105,6 @@ static QVector getIncludedServiceIds(const ComPtr &buffer, bool isWCharString = false) -{ - ComPtr byteAccess; - HRESULT hr = buffer.As(&byteAccess); - Q_ASSERT_SUCCEEDED(hr); - char *data; - hr = byteAccess->Buffer(reinterpret_cast(&data)); - Q_ASSERT_SUCCEEDED(hr); - UINT32 size; - hr = buffer->get_Length(&size); - Q_ASSERT_SUCCEEDED(hr); - if (isWCharString) { - QString valueString = QString::fromUtf16(reinterpret_cast(data)).left(size / 2); - return valueString.toUtf8(); - } - return QByteArray(data, size); -} - static QByteArray byteArrayFromGattResult(const ComPtr &gattResult, bool isWCharString = false) { ComPtr buffer; diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index 5501c4f3..45a80252 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -39,6 +39,7 @@ #include "qlowenergycontroller_winrt_new_p.h" #include "qlowenergycontroller_winrt_p.h" +#include "qbluetoothutils_winrt_p.h" #include #include @@ -119,37 +120,6 @@ QLowEnergyControllerPrivate *createWinRTLowEnergyController() return new QLowEnergyControllerPrivateWinRT(); } -static QByteArray byteArrayFromBuffer(const ComPtr &buffer, bool isWCharString = false) -{ - if (!buffer) { - qCWarning(QT_BT_WINRT) << "nullptr passed to byteArrayFromBuffer"; - return QByteArray(); - } - ComPtr byteAccess; - HRESULT hr = buffer.As(&byteAccess); - if (FAILED(hr)) { - qCWarning(QT_BT_WINRT) << "Could not cast Buffer to ByteAccess"; - return QByteArray(); - } - char *data; - hr = byteAccess->Buffer(reinterpret_cast(&data)); - if (FAILED(hr)) { - qCWarning(QT_BT_WINRT) << "Could not obtain buffer data"; - return QByteArray(); - } - UINT32 size; - hr = buffer->get_Length(&size); - if (FAILED(hr)) { - qCWarning(QT_BT_WINRT) << "Could not obtain buffer length"; - return QByteArray(); - } - if (isWCharString) { - QString valueString = QString::fromUtf16(reinterpret_cast(data)).left(size / 2); - return valueString.toUtf8(); - } - return QByteArray(data, int(size)); -} - static QByteArray byteArrayFromGattResult(const ComPtr &gattResult, bool isWCharString = false) { -- cgit v1.2.3 From d37dbf7727e1e7f3dce70d5855ec407e2e8a6e54 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Wed, 3 Apr 2019 13:36:39 +0200 Subject: winrt: Remove unused function Change-Id: I795c9b60454c350c56a4dd362b5b359b12a3a8f9 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt.cpp | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt.cpp b/src/bluetooth/qlowenergycontroller_winrt.cpp index a9eb523b..f4a5d0cc 100644 --- a/src/bluetooth/qlowenergycontroller_winrt.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt.cpp @@ -77,34 +77,6 @@ typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharCo Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT) -static QVector getIncludedServiceIds(const ComPtr &service) -{ - QVector result; - ComPtr service2; - HRESULT hr = service.As(&service2); - Q_ASSERT_SUCCEEDED(hr); - ComPtr> includedServices; - hr = service2->GetAllIncludedServices(&includedServices); - Q_ASSERT_SUCCEEDED(hr); - - uint count; - hr = includedServices->get_Size(&count); - Q_ASSERT_SUCCEEDED(hr); - for (uint i = 0; i < count; ++i) { - ComPtr includedService; - hr = includedServices->GetAt(i, &includedService); - Q_ASSERT_SUCCEEDED(hr); - GUID guuid; - hr = includedService->get_Uuid(&guuid); - Q_ASSERT_SUCCEEDED(hr); - const QBluetoothUuid service(guuid); - result << service; - - result << getIncludedServiceIds(includedService); - } - return result; -} - static QByteArray byteArrayFromGattResult(const ComPtr &gattResult, bool isWCharString = false) { ComPtr buffer; -- cgit v1.2.3 From 717b95c7863278332ec4eb4dfbbfb6147a5ac9ed Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Wed, 10 Apr 2019 13:02:00 +0200 Subject: CoreBluetooth: add a missing -peripheral:didModifyServices: method With some peculiar device we suddenly (during the service details discovery) got a crash with CBDescriptor suddenly becoming something else - NSString, NSMutableArray etc. - meaning the object was deleted and its memory re-used. It would appear, CBPeripheral can suddenly change it's services tree and it informs its delegate (aka 'us') about this change using the (previously) missing method. In this method we cannot do much, due to the specificity of our public API that allows concurrent discoveries, it's 'non-monolitic' (in several steps) discoveries etc. etc. So the only thing we can do - stop everything, remove all services, transition to QLowEnergyController::ConnectedState and wait for a user to re-discover services. Fixes: QTBUG-75043 Change-Id: Ie98d90aea112e40b4c6771e3f7315772dfd92b39 Reviewed-by: Alex Blasche --- src/bluetooth/osx/osxbtcentralmanager.mm | 24 ++++++++++++++++++++++++ src/bluetooth/osx/osxbtnotifier_p.h | 1 + src/bluetooth/qlowenergycontroller_osx.mm | 17 +++++++++++++++++ src/bluetooth/qlowenergycontroller_osx_p.h | 1 + 4 files changed, 43 insertions(+) diff --git a/src/bluetooth/osx/osxbtcentralmanager.mm b/src/bluetooth/osx/osxbtcentralmanager.mm index 41713909..6b742684 100644 --- a/src/bluetooth/osx/osxbtcentralmanager.mm +++ b/src/bluetooth/osx/osxbtcentralmanager.mm @@ -1373,6 +1373,30 @@ QT_USE_NAMESPACE [self discoverIncludedServices]; } +- (void)peripheral:(CBPeripheral *)aPeripheral + didModifyServices:(NSArray *)invalidatedServices +{ + Q_UNUSED(aPeripheral) + Q_UNUSED(invalidatedServices) + + qCWarning(QT_BT_OSX) << "The peripheral has modified its services."; + // "This method is invoked whenever one or more services of a peripheral have changed. + // A peripheral’s services have changed if: + // * A service is removed from the peripheral’s database + // * A new service is added to the peripheral’s database + // * A service that was previously removed from the peripheral’s + // database is readded to the database at a different location" + + // In case new services were added - we have to discover them. + // In case some were removed - we can end up with dangling pointers + // (see our 'watchdogs', for example). To handle the situation + // we stop all current operations here, report to QLowEnergyController + // so that it can trigger re-discovery. + [self reset]; + managerState = OSXBluetooth::CentralManagerIdle; + if (notifier) + emit notifier->servicesWereModified(); +} - (void)peripheral:(CBPeripheral *)aPeripheral didDiscoverIncludedServicesForService:(CBService *)service error:(NSError *)error diff --git a/src/bluetooth/osx/osxbtnotifier_p.h b/src/bluetooth/osx/osxbtnotifier_p.h index 47ee6ba1..dca6c268 100644 --- a/src/bluetooth/osx/osxbtnotifier_p.h +++ b/src/bluetooth/osx/osxbtnotifier_p.h @@ -89,6 +89,7 @@ Q_SIGNALS: void descriptorRead(QLowEnergyHandle descHandle, const QByteArray &value); void descriptorWritten(QLowEnergyHandle descHandle, const QByteArray &value); void notificationEnabled(QLowEnergyHandle charHandle, bool enabled); + void servicesWereModified(); void LEnotSupported(); void CBManagerError(QBluetoothDeviceDiscoveryAgent::Error error); diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_osx.mm index 8cef621c..8bcdc22e 100644 --- a/src/bluetooth/qlowenergycontroller_osx.mm +++ b/src/bluetooth/qlowenergycontroller_osx.mm @@ -339,6 +339,21 @@ void QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished(QSharedP qtService->setState(QLowEnergyService::ServiceDiscovered); } +void QLowEnergyControllerPrivateOSX::_q_servicesWereModified() +{ + if (!(controllerState == QLowEnergyController::DiscoveringState + || controllerState == QLowEnergyController::DiscoveredState)) { + qCWarning(QT_BT_OSX) << "services were modified while controller is not in Discovered/Discovering state"; + return; + } + + if (controllerState == QLowEnergyController::DiscoveredState) + invalidateServices(); + + controllerState = QLowEnergyController::ConnectedState; + q_ptr->discoverServices(); +} + void QLowEnergyControllerPrivateOSX::_q_characteristicRead(QLowEnergyHandle charHandle, const QByteArray &value) { @@ -989,6 +1004,8 @@ bool QLowEnergyControllerPrivateOSX::connectSlots(OSXBluetooth::LECBManagerNotif this, &QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished); ok = ok && connect(notifier, &LECBManagerNotifier::serviceDetailsDiscoveryFinished, this, &QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished); + ok = ok && connect(notifier, &LECBManagerNotifier::servicesWereModified, + this, &QLowEnergyControllerPrivateOSX::_q_servicesWereModified); ok = ok && connect(notifier, &LECBManagerNotifier::characteristicRead, this, &QLowEnergyControllerPrivateOSX::_q_characteristicRead); ok = ok && connect(notifier, &LECBManagerNotifier::characteristicWritten, diff --git a/src/bluetooth/qlowenergycontroller_osx_p.h b/src/bluetooth/qlowenergycontroller_osx_p.h index 24b7c6e9..da959895 100644 --- a/src/bluetooth/qlowenergycontroller_osx_p.h +++ b/src/bluetooth/qlowenergycontroller_osx_p.h @@ -98,6 +98,7 @@ private Q_SLOTS: void _q_serviceDiscoveryFinished(); void _q_serviceDetailsDiscoveryFinished(QSharedPointer service); + void _q_servicesWereModified(); void _q_characteristicRead(QLowEnergyHandle charHandle, const QByteArray &value); void _q_characteristicWritten(QLowEnergyHandle charHandle, const QByteArray &value); -- cgit v1.2.3 From 576164fef388f312dcc7f59721de8237dfcd15b3 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Thu, 18 Apr 2019 10:19:40 +0200 Subject: Create qbluetoothlocaldevice_winrt In preparation for the followup patches. Change-Id: I9b1f6c181adb847f6aafdaf60fcef7139a12b638 Reviewed-by: Alex Blasche --- src/bluetooth/bluetooth.pro | 2 +- src/bluetooth/qbluetoothlocaldevice_p.cpp | 10 +- src/bluetooth/qbluetoothlocaldevice_p.h | 19 +++- src/bluetooth/qbluetoothlocaldevice_winrt.cpp | 127 ++++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 src/bluetooth/qbluetoothlocaldevice_winrt.cpp diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index 2bcdee12..b104a902 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -224,7 +224,7 @@ qtConfig(bluez) { SOURCES += \ qbluetoothdevicediscoveryagent_winrt.cpp \ - qbluetoothlocaldevice_p.cpp \ + qbluetoothlocaldevice_winrt.cpp \ qbluetoothserver_winrt.cpp \ qbluetoothservicediscoveryagent_winrt.cpp \ qbluetoothserviceinfo_winrt.cpp \ diff --git a/src/bluetooth/qbluetoothlocaldevice_p.cpp b/src/bluetooth/qbluetoothlocaldevice_p.cpp index 793a8311..fa4a509e 100644 --- a/src/bluetooth/qbluetoothlocaldevice_p.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_p.cpp @@ -51,7 +51,7 @@ QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) : QObject(parent), d_ptr(new QBluetoothLocalDevicePrivate(this, QBluetoothAddress())) { -#if !defined(QT_IOS_BLUETOOTH) && !defined(QT_WINRT_BLUETOOTH) +#if !defined(QT_IOS_BLUETOOTH) printDummyWarning(); #endif registerQBluetoothLocalDeviceMetaType(); @@ -85,11 +85,7 @@ void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode mode) QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const { -#ifdef QT_WINRT_BLUETOOTH - return HostConnectable; -#else return HostPoweredOff; -#endif } QList QBluetoothLocalDevice::connectedDevices() const @@ -116,11 +112,7 @@ QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus( const QBluetoothAddress &address) const { Q_UNUSED(address); -#ifdef QT_WINRT_BLUETOOTH - return Paired; -#else return Unpaired; -#endif } void QBluetoothLocalDevice::pairingConfirmation(bool confirmation) diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h index 98c62151..9b478b84 100644 --- a/src/bluetooth/qbluetoothlocaldevice_p.h +++ b/src/bluetooth/qbluetoothlocaldevice_p.h @@ -208,7 +208,20 @@ private: void initializeAdapter(); void initializeAdapterBluez5(); }; -#elif !defined(QT_OSX_BLUETOOTH) // winrt and dummy backend +#elif defined(QT_WINRT_BLUETOOTH) +class QBluetoothLocalDevicePrivate : public QObject +{ + Q_DECLARE_PUBLIC(QBluetoothLocalDevice) +public: + QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q, + QBluetoothAddress = QBluetoothAddress()); + + bool isValid() const; + +private: + QBluetoothLocalDevice *q_ptr; +}; +#elif !defined(QT_OSX_BLUETOOTH) // dummy backend class QBluetoothLocalDevicePrivate : public QObject { public: @@ -219,11 +232,7 @@ public: bool isValid() const { -#ifndef QT_WINRT_BLUETOOTH return false; -#else - return true; -#endif } }; #endif diff --git a/src/bluetooth/qbluetoothlocaldevice_winrt.cpp b/src/bluetooth/qbluetoothlocaldevice_winrt.cpp new file mode 100644 index 00000000..dbd17f50 --- /dev/null +++ b/src/bluetooth/qbluetoothlocaldevice_winrt.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbluetoothlocaldevice.h" +#include "qbluetoothaddress.h" + +#include "qbluetoothlocaldevice_p.h" + +QT_BEGIN_NAMESPACE + +QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) : + QObject(parent), + d_ptr(new QBluetoothLocalDevicePrivate(this, QBluetoothAddress())) +{ + registerQBluetoothLocalDeviceMetaType(); +} + +QBluetoothLocalDevice::QBluetoothLocalDevice(const QBluetoothAddress &address, QObject *parent) : + QObject(parent), + d_ptr(new QBluetoothLocalDevicePrivate(this, address)) +{ + registerQBluetoothLocalDeviceMetaType(); +} + +QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q, QBluetoothAddress) + : q_ptr(q) +{ +} + +bool QBluetoothLocalDevicePrivate::isValid() const +{ + return true; +} + +void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pairing pairing) +{ + Q_UNUSED(address); + Q_UNUSED(pairing); + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QBluetoothLocalDevice::Error, + QBluetoothLocalDevice::PairingError)); +} + +QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus( + const QBluetoothAddress &address) const +{ + Q_UNUSED(address); + return QBluetoothLocalDevice::Unpaired; +} + +void QBluetoothLocalDevice::pairingConfirmation(bool confirmation) +{ + Q_UNUSED(confirmation); +} + +void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode mode) +{ + Q_UNUSED(mode); +} + +QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const +{ + return HostConnectable; +} + +QList QBluetoothLocalDevice::connectedDevices() const +{ + return QList(); +} + +void QBluetoothLocalDevice::powerOn() +{ +} + +QString QBluetoothLocalDevice::name() const +{ + return QString(); +} + +QBluetoothAddress QBluetoothLocalDevice::address() const +{ + return QBluetoothAddress(); +} + +QList QBluetoothLocalDevice::allDevices() +{ + QList localDevices; + return localDevices; +} + +QT_END_NAMESPACE -- cgit v1.2.3 From 23386ed8dab0a8b8280c2e1b935171adcce0d833 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Thu, 18 Apr 2019 10:28:43 +0200 Subject: winrt: Add QBluetoothLocalDevice::pairingStatus Task-number: QTBUG-62294 Change-Id: I61ee7dc30996c8e12c0fa75f7c85931a61c12554 Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothlocaldevice_p.h | 17 ++++++ src/bluetooth/qbluetoothlocaldevice_winrt.cpp | 81 ++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h index 9b478b84..8f3e2b43 100644 --- a/src/bluetooth/qbluetoothlocaldevice_p.h +++ b/src/bluetooth/qbluetoothlocaldevice_p.h @@ -84,6 +84,21 @@ QT_END_NAMESPACE #include #endif +#ifdef QT_WINRT_BLUETOOTH +#include + +namespace ABI { + namespace Windows { + namespace Devices { + namespace Bluetooth { + struct IBluetoothDeviceStatics; + struct IBluetoothLEDeviceStatics; + } + } + } +} +#endif + QT_BEGIN_NAMESPACE extern void registerQBluetoothLocalDeviceMetaType(); @@ -220,6 +235,8 @@ public: private: QBluetoothLocalDevice *q_ptr; + Microsoft::WRL::ComPtr mStatics; + Microsoft::WRL::ComPtr mLEStatics; }; #elif !defined(QT_OSX_BLUETOOTH) // dummy backend class QBluetoothLocalDevicePrivate : public QObject diff --git a/src/bluetooth/qbluetoothlocaldevice_winrt.cpp b/src/bluetooth/qbluetoothlocaldevice_winrt.cpp index dbd17f50..ae794db0 100644 --- a/src/bluetooth/qbluetoothlocaldevice_winrt.cpp +++ b/src/bluetooth/qbluetoothlocaldevice_winrt.cpp @@ -42,8 +42,57 @@ #include "qbluetoothlocaldevice_p.h" +#ifdef CLASSIC_APP_BUILD +#define Q_OS_WINRT +#endif +#include + +#include +#include +#include + +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Devices::Bluetooth; +using namespace ABI::Windows::Devices::Enumeration; +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; + QT_BEGIN_NAMESPACE +template +ComPtr getPairingInfo(ComPtr deviceStatics, + const QBluetoothAddress &address) +{ + ComPtr> op; + if (!deviceStatics) + return nullptr; + HRESULT hr = deviceStatics->FromBluetoothAddressAsync(address.toUInt64(), &op); + RETURN_IF_FAILED("Could not obtain device from address", return nullptr); + ComPtr device; + hr = QWinRTFunctions::await(op, device.GetAddressOf(), + QWinRTFunctions::ProcessMainThreadEvents, 5000); + if (FAILED(hr) || !device) { + qErrnoWarning("Could not obtain device from address"); + return nullptr; + } + ComPtr device2; + hr = device.As(&device2); + RETURN_IF_FAILED("Could not cast device", return nullptr); + ComPtr deviceInfo; + hr = device2->get_DeviceInformation(&deviceInfo); + if (FAILED(hr) || !deviceInfo) { + qErrnoWarning("Could not obtain device information"); + return nullptr; + } + ComPtr deviceInfo2; + hr = deviceInfo.As(&deviceInfo2); + RETURN_IF_FAILED("Could not cast device information", return nullptr); + ComPtr pairingInfo; + hr = deviceInfo2->get_Pairing(&pairingInfo); + RETURN_IF_FAILED("Could not obtain pairing information", return nullptr); + return pairingInfo; +} + QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) : QObject(parent), d_ptr(new QBluetoothLocalDevicePrivate(this, QBluetoothAddress())) @@ -61,11 +110,13 @@ QBluetoothLocalDevice::QBluetoothLocalDevice(const QBluetoothAddress &address, Q QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q, QBluetoothAddress) : q_ptr(q) { + GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice).Get(), &mLEStatics); + GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothDevice).Get(), &mStatics); } bool QBluetoothLocalDevicePrivate::isValid() const { - return true; + return (mStatics != nullptr && mLEStatics != nullptr); } void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pairing pairing) @@ -80,8 +131,32 @@ void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pai QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus( const QBluetoothAddress &address) const { - Q_UNUSED(address); - return QBluetoothLocalDevice::Unpaired; + if (!isValid() || address.isNull()) + return QBluetoothLocalDevice::Unpaired; + + ComPtr pairingInfo = getPairingInfo(d_ptr->mLEStatics, address); + if (!pairingInfo) + pairingInfo = getPairingInfo(d_ptr->mStatics, address); + if (!pairingInfo) + return QBluetoothLocalDevice::Unpaired; + boolean isPaired; + HRESULT hr = pairingInfo->get_IsPaired(&isPaired); + RETURN_IF_FAILED("Could not obtain device pairing", return QBluetoothLocalDevice::Unpaired); + if (!isPaired) + return QBluetoothLocalDevice::Unpaired; + + ComPtr pairingInfo2; + hr = pairingInfo.As(&pairingInfo2); + RETURN_IF_FAILED("Could not cast pairing info", return QBluetoothLocalDevice::Paired); + DevicePairingProtectionLevel protection = DevicePairingProtectionLevel_None; + hr = pairingInfo2->get_ProtectionLevel(&protection); + RETURN_IF_FAILED("Could not obtain pairing protection level", return QBluetoothLocalDevice::Paired); + if (protection == DevicePairingProtectionLevel_Encryption + || protection == DevicePairingProtectionLevel_EncryptionAndAuthentication) + return QBluetoothLocalDevice::AuthorizedPaired; + return QBluetoothLocalDevice::Paired; } void QBluetoothLocalDevice::pairingConfirmation(bool confirmation) -- cgit v1.2.3 From 5aa37aab69d89691ab7eded7f708915612f1afa3 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Thu, 25 Apr 2019 10:04:29 +0200 Subject: qlowenergycontroller_winrt_new: Add registerStatusChanges and onStatusChange functions In preparation for following patches, functionality related to status changes was moved into dedicated functions. That makes code more readable and avoids late callbacks which can happen when lambdas are used. Change-Id: Ie699adef238013bb5391b57a1794e0b3d6bf8312 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 79 +++++++++++++++--------- src/bluetooth/qlowenergycontroller_winrt_new_p.h | 2 + 2 files changed, 52 insertions(+), 29 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index 45a80252..c3efec7f 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -473,35 +473,12 @@ void QLowEnergyControllerPrivateWinRTNew::connectToDevice() BluetoothConnectionStatus status; hr = mDevice->get_ConnectionStatus(&status); CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain device's connection status", return) - hr = QEventDispatcherWinRT::runOnXamlThread([this, q]() { - HRESULT hr; - hr = mDevice->add_ConnectionStatusChanged( - Callback([this, q](IBluetoothLEDevice *dev, IInspectable *) { - BluetoothConnectionStatus status; - HRESULT hr; - hr = dev->get_ConnectionStatus(&status); - CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain connection status", return S_OK) - if (state == QLowEnergyController::ConnectingState - && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) { - setState(QLowEnergyController::ConnectedState); - emit q->connected(); - } else if (state != QLowEnergyController::UnconnectedState - && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) { - invalidateServices(); - unregisterFromValueChanges(); - unregisterFromStatusChanges(); - mDevice = nullptr; - setError(QLowEnergyController::RemoteHostClosedError); - setState(QLowEnergyController::UnconnectedState); - emit q->disconnected(); - } - return S_OK; - }).Get(), &mStatusChangedToken); - CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not register connection status callback", return S_OK) - return S_OK; - }); - CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not add status callback on Xaml thread", return) - + if (!registerForStatusChanges()) { + qCWarning(QT_BT_WINRT) << "Could not register status changes"; + setError(QLowEnergyController::ConnectionError); + setState(QLowEnergyController::UnconnectedState); + return; + } if (status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) { setState(QLowEnergyController::ConnectedState); emit q->connected(); @@ -726,6 +703,26 @@ void QLowEnergyControllerPrivateWinRTNew::unregisterFromValueChanges() mValueChangedTokens.clear(); } +bool QLowEnergyControllerPrivateWinRTNew::registerForStatusChanges() +{ + if (!mDevice) + return false; + + qCDebug(QT_BT_WINRT) << __FUNCTION__; + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([this]() { + HRESULT hr; + hr = mDevice->add_ConnectionStatusChanged( + Callback(this, &QLowEnergyControllerPrivateWinRTNew::onStatusChange).Get(), + &mStatusChangedToken); + RETURN_IF_FAILED("Could not register connection status callback", return hr) + return S_OK; + }); + RETURN_FALSE_IF_FAILED("Could not add status callback on Xaml thread") + return true; +} + void QLowEnergyControllerPrivateWinRTNew::unregisterFromStatusChanges() { qCDebug(QT_BT_WINRT) << __FUNCTION__; @@ -735,6 +732,30 @@ void QLowEnergyControllerPrivateWinRTNew::unregisterFromStatusChanges() } } +HRESULT QLowEnergyControllerPrivateWinRTNew::onStatusChange(IBluetoothLEDevice *dev, IInspectable *) +{ + Q_Q(QLowEnergyController); + BluetoothConnectionStatus status; + HRESULT hr; + hr = dev->get_ConnectionStatus(&status); + RETURN_IF_FAILED("Could not obtain connection status", return S_OK) + if (state == QLowEnergyController::ConnectingState + && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) { + setState(QLowEnergyController::ConnectedState); + emit q->connected(); + } else if (state != QLowEnergyController::UnconnectedState + && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) { + invalidateServices(); + unregisterFromValueChanges(); + unregisterFromStatusChanges(); + mDevice = nullptr; + setError(QLowEnergyController::RemoteHostClosedError); + setState(QLowEnergyController::UnconnectedState); + emit q->disconnected(); + } + return S_OK; +} + void QLowEnergyControllerPrivateWinRTNew::obtainIncludedServices( QSharedPointer servicePointer, ComPtr service) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new_p.h b/src/bluetooth/qlowenergycontroller_winrt_new_p.h index bf409745..69ee3fca 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new_p.h +++ b/src/bluetooth/qlowenergycontroller_winrt_new_p.h @@ -143,7 +143,9 @@ private: void registerForValueChanges(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid); void unregisterFromValueChanges(); + bool registerForStatusChanges(); void unregisterFromStatusChanges(); + HRESULT onStatusChange(ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice *dev, IInspectable *); void obtainIncludedServices(QSharedPointer servicePointer, Microsoft::WRL::ComPtr nativeService); -- cgit v1.2.3 From 68cb332228df1c8730e721061134c18bda37cf02 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Wed, 17 Apr 2019 12:17:21 +0200 Subject: qlowenergycontroller_winrt_new: Avoid late callbacks that lead to crashes Users may run into crashes on device disconnects in case that we run into a callback while the disconnects happen. Thus we have to avoid calling functions on deleted objects by avoiding lambdas if possible or using QPointers in lambda captures. Change-Id: Idcd3781bd396d4ef785191e4c65bae20e5149c04 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 174 ++++++++++++----------- src/bluetooth/qlowenergycontroller_winrt_new_p.h | 6 +- 2 files changed, 97 insertions(+), 83 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index c3efec7f..fb83371b 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -91,15 +91,18 @@ typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharCo continue; \ } -#define CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, msg, ret) \ +#define CHECK_FOR_DEVICE_CONNECTION_ERROR_IMPL(this, hr, msg, ret) \ if (FAILED(hr)) { \ qCWarning(QT_BT_WINRT) << msg; \ - unregisterFromStatusChanges(); \ - setError(QLowEnergyController::ConnectionError); \ - setState(QLowEnergyController::UnconnectedState); \ + this->unregisterFromStatusChanges(); \ + this->setError(QLowEnergyController::ConnectionError); \ + this->setState(QLowEnergyController::UnconnectedState); \ ret; \ } +#define CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, msg, ret) \ + CHECK_FOR_DEVICE_CONNECTION_ERROR_IMPL(this, hr, msg, ret) + #define CHECK_HR_AND_SET_SERVICE_ERROR(hr, msg, service, error, ret) \ if (FAILED(hr)) { \ qCDebug(QT_BT_WINRT) << msg; \ @@ -668,18 +671,8 @@ void QLowEnergyControllerPrivateWinRTNew::registerForValueChanges(const QBluetoo EventRegistrationToken token; HRESULT hr; hr = characteristic->add_ValueChanged( - Callback( - [this](IGattCharacteristic *characteristic, IGattValueChangedEventArgs *args) { - HRESULT hr; - quint16 handle; - hr = characteristic->get_AttributeHandle(&handle); - RETURN_IF_FAILED("Could not obtain characteristic's handle", return S_OK) - ComPtr buffer; - hr = args->get_CharacteristicValue(&buffer); - RETURN_IF_FAILED("Could not obtain characteristic's value", return S_OK) - characteristicChanged(handle, byteArrayFromBuffer(buffer)); - return S_OK; - }).Get(), &token); + Callback(this, &QLowEnergyControllerPrivateWinRTNew::onValueChange).Get(), + &token); RETURN_IF_FAILED("Could not register characteristic for value changes", return) mValueChangedTokens.append(ValueChangedEntry(characteristic, token)); qCDebug(QT_BT_WINRT) << "Characteristic" << charUuid << "in service" @@ -703,6 +696,19 @@ void QLowEnergyControllerPrivateWinRTNew::unregisterFromValueChanges() mValueChangedTokens.clear(); } +HRESULT QLowEnergyControllerPrivateWinRTNew::onValueChange(IGattCharacteristic *characteristic, IGattValueChangedEventArgs *args) +{ + HRESULT hr; + quint16 handle; + hr = characteristic->get_AttributeHandle(&handle); + RETURN_IF_FAILED("Could not obtain characteristic's handle", return S_OK) + ComPtr buffer; + hr = args->get_CharacteristicValue(&buffer); + RETURN_IF_FAILED("Could not obtain characteristic's value", return S_OK) + characteristicChanged(handle, byteArrayFromBuffer(buffer)); + return S_OK; +} + bool QLowEnergyControllerPrivateWinRTNew::registerForStatusChanges() { if (!mDevice) @@ -812,10 +818,68 @@ void QLowEnergyControllerPrivateWinRTNew::obtainIncludedServices( } } -void QLowEnergyControllerPrivateWinRTNew::discoverServices() +HRESULT QLowEnergyControllerPrivateWinRTNew::onServiceDiscoveryFinished(ABI::Windows::Foundation::IAsyncOperation *op, AsyncStatus status) { Q_Q(QLowEnergyController); + if (status != AsyncStatus::Completed) { + qCDebug(QT_BT_WINRT) << "Could not obtain services"; + return S_OK; + } + ComPtr result; + ComPtr> deviceServices; + HRESULT hr = op->GetResults(&result); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service discovery result", + return S_OK); + GattCommunicationStatus commStatus; + hr = result->get_Status(&commStatus); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service discovery status", + return S_OK); + if (commStatus != GattCommunicationStatus_Success) + return S_OK; + + hr = result->get_Services(&deviceServices); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service list", + return S_OK); + + uint serviceCount; + hr = deviceServices->get_Size(&serviceCount); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service list size", + return S_OK); + for (uint i = 0; i < serviceCount; ++i) { + ComPtr deviceService; + hr = deviceServices->GetAt(i, &deviceService); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service"); + GUID guuid; + hr = deviceService->get_Uuid(&guuid); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service's Uuid"); + const QBluetoothUuid service(guuid); + + QSharedPointer pointer; + if (serviceList.contains(service)) { + pointer = serviceList.value(service); + } else { + QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate(); + priv->uuid = service; + priv->setController(this); + pointer = QSharedPointer(priv); + serviceList.insert(service, pointer); + } + pointer->type |= QLowEnergyService::PrimaryService; + + obtainIncludedServices(pointer, deviceService); + + emit q->serviceDiscovered(service); + } + + setState(QLowEnergyController::DiscoveredState); + emit q->discoveryFinished(); + + return S_OK; +} + +void QLowEnergyControllerPrivateWinRTNew::discoverServices() +{ qCDebug(QT_BT_WINRT) << "Service discovery initiated"; ComPtr device3; @@ -824,67 +888,10 @@ void QLowEnergyControllerPrivateWinRTNew::discoverServices() ComPtr> asyncResult; hr = device3->GetGattServicesAsync(&asyncResult); CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain services", return); - hr = QEventDispatcherWinRT::runOnXamlThread( [asyncResult, q, this] () { + hr = QEventDispatcherWinRT::runOnXamlThread( [asyncResult, this] () { HRESULT hr = asyncResult->put_Completed( Callback>( - [this, q](IAsyncOperation *op, - AsyncStatus status) { - if (status != AsyncStatus::Completed) { - qCDebug(QT_BT_WINRT) << "Could not obtain services"; - return S_OK; - } - ComPtr result; - ComPtr> deviceServices; - HRESULT hr = op->GetResults(&result); - CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service discovery result", - return S_OK); - GattCommunicationStatus commStatus; - hr = result->get_Status(&commStatus); - CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service discovery status", - return S_OK); - if (commStatus != GattCommunicationStatus_Success) - return S_OK; - - hr = result->get_Services(&deviceServices); - CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service list", - return S_OK); - - uint serviceCount; - hr = deviceServices->get_Size(&serviceCount); - CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service list size", - return S_OK); - for (uint i = 0; i < serviceCount; ++i) { - ComPtr deviceService; - hr = deviceServices->GetAt(i, &deviceService); - WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service"); - GUID guuid; - hr = deviceService->get_Uuid(&guuid); - WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service's Uuid"); - const QBluetoothUuid service(guuid); - - QSharedPointer pointer; - if (serviceList.contains(service)) { - pointer = serviceList.value(service); - } else { - QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate(); - priv->uuid = service; - priv->setController(this); - - pointer = QSharedPointer(priv); - serviceList.insert(service, pointer); - } - pointer->type |= QLowEnergyService::PrimaryService; - - obtainIncludedServices(pointer, deviceService); - - emit q->serviceDiscovered(service); - } - - setState(QLowEnergyController::DiscoveredState); - emit q->discoveryFinished(); - - return S_OK; - }).Get()); + this, &QLowEnergyControllerPrivateWinRTNew::onServiceDiscoveryFinished).Get()); CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not register service discovery callback", return S_OK) return hr; @@ -1326,7 +1333,8 @@ void QLowEnergyControllerPrivateWinRTNew::writeCharacteristic( hr = characteristic->WriteValueWithOptionAsync(buffer.Get(), option, &writeOp); CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could write characteristic", service, QLowEnergyService::CharacteristicWriteError, return S_OK) - auto writeCompletedLambda =[charData, charHandle, newValue, service, writeWithResponse, this] + QPointer thisPtr(this); + auto writeCompletedLambda = [charData, charHandle, newValue, service, writeWithResponse, thisPtr] (IAsyncOperation *op, AsyncStatus status) { if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { @@ -1353,7 +1361,7 @@ void QLowEnergyControllerPrivateWinRTNew::writeCharacteristic( // only update cache when property is readable. Otherwise it remains // empty. if (charData.properties & QLowEnergyCharacteristic::Read) - updateValueOfCharacteristic(charHandle, newValue, false); + thisPtr->updateValueOfCharacteristic(charHandle, newValue, false); if (writeWithResponse) emit service->characteristicWritten(QLowEnergyCharacteristic(service, charHandle), newValue); @@ -1433,7 +1441,8 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor( HRESULT hr = characteristic->WriteClientCharacteristicConfigurationDescriptorAsync(value, &writeOp); CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not write client characteristic configuration", service, QLowEnergyService::DescriptorWriteError, return S_OK) - auto writeCompletedLambda = [charHandle, descHandle, newValue, service, this] + QPointer thisPtr(this); + auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr] (IAsyncOperation *op, AsyncStatus status) { if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { @@ -1451,7 +1460,7 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor( service->setError(QLowEnergyService::DescriptorWriteError); return S_OK; } - updateValueOfDescriptor(charHandle, descHandle, newValue, false); + thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue, false); emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle), newValue); return S_OK; @@ -1526,7 +1535,8 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor( hr = descriptor->WriteValueAsync(buffer.Get(), &writeOp); CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not write descriptor value", service, QLowEnergyService::DescriptorWriteError, return S_OK) - auto writeCompletedLambda = [charHandle, descHandle, newValue, service, this] + QPointer thisPtr(this); + auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr] (IAsyncOperation *op, AsyncStatus status) { if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { @@ -1544,7 +1554,7 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor( service->setError(QLowEnergyService::DescriptorWriteError); return S_OK; } - updateValueOfDescriptor(charHandle, descHandle, newValue, false); + thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue, false); emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle), newValue); return S_OK; diff --git a/src/bluetooth/qlowenergycontroller_winrt_new_p.h b/src/bluetooth/qlowenergycontroller_winrt_new_p.h index 69ee3fca..fa8e516f 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new_p.h +++ b/src/bluetooth/qlowenergycontroller_winrt_new_p.h @@ -62,6 +62,7 @@ #include #include +#include #include @@ -142,6 +143,8 @@ private: void registerForValueChanges(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid); void unregisterFromValueChanges(); + HRESULT onValueChange(ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattCharacteristic *characteristic, + ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::IGattValueChangedEventArgs *args); bool registerForStatusChanges(); void unregisterFromStatusChanges(); @@ -149,7 +152,8 @@ private: void obtainIncludedServices(QSharedPointer servicePointer, Microsoft::WRL::ComPtr nativeService); - + HRESULT onServiceDiscoveryFinished(ABI::Windows::Foundation::IAsyncOperation *op, + AsyncStatus status); }; QT_END_NAMESPACE -- cgit v1.2.3 From 1d4cb8a6782b74a379098da58f383aaaf61989bd Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Tue, 9 Apr 2019 09:58:50 +0200 Subject: winrt: Try "connectToDevice" indefinitely connectToDevice should not fail after a timeout, but try to connect to the device indefinitely. Unfortunately the behavior depends on the device's pairing status so two connectToDevice functions are needed. But the current implementations potentially wait indefinitely until the connection can be established. Change-Id: Iacb81e2c995974020b14d297528e54c326eb0453 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 267 ++++++++++++++--------- src/bluetooth/qlowenergycontroller_winrt_new_p.h | 4 + 2 files changed, 168 insertions(+), 103 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index fb83371b..7bff47be 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -41,6 +41,7 @@ #include "qlowenergycontroller_winrt_p.h" #include "qbluetoothutils_winrt_p.h" +#include #include #include #include @@ -439,6 +440,7 @@ QLowEnergyControllerPrivateWinRTNew::~QLowEnergyControllerPrivateWinRTNew() { unregisterFromStatusChanges(); unregisterFromValueChanges(); + mAbortPending = true; } void QLowEnergyControllerPrivateWinRTNew::init() @@ -448,6 +450,7 @@ void QLowEnergyControllerPrivateWinRTNew::init() void QLowEnergyControllerPrivateWinRTNew::connectToDevice() { qCDebug(QT_BT_WINRT) << __FUNCTION__; + mAbortPending = false; Q_Q(QLowEnergyController); if (remoteDevice.isNull()) { qWarning() << "Invalid/null remote device address"; @@ -476,115 +479,18 @@ void QLowEnergyControllerPrivateWinRTNew::connectToDevice() BluetoothConnectionStatus status; hr = mDevice->get_ConnectionStatus(&status); CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain device's connection status", return) - if (!registerForStatusChanges()) { - qCWarning(QT_BT_WINRT) << "Could not register status changes"; - setError(QLowEnergyController::ConnectionError); - setState(QLowEnergyController::UnconnectedState); - return; - } if (status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) { setState(QLowEnergyController::ConnectedState); emit q->connected(); return; } - ComPtr device3; - hr = mDevice.As(&device3); - CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast device", return) - ComPtr> deviceServicesOp; - hr = device3->GetGattServicesAsync(&deviceServicesOp); - CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain services", return) - ComPtr deviceServicesResult; - hr = QWinRTFunctions::await(deviceServicesOp, deviceServicesResult.GetAddressOf(), - QWinRTFunctions::ProcessMainThreadEvents, 5000); - CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await services operation", return) - - GattCommunicationStatus commStatus; - hr = deviceServicesResult->get_Status(&commStatus); - if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) { - qCWarning(QT_BT_WINRT()) << "Service operation failed"; - setError(QLowEnergyController::ConnectionError); - setState(QLowEnergyController::UnconnectedState); - unregisterFromStatusChanges(); - return; - } - - ComPtr> deviceServices; - hr = deviceServicesResult->get_Services(&deviceServices); - CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain list of services", return) - uint serviceCount; - hr = deviceServices->get_Size(&serviceCount); - CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service count", return) - - // Windows automatically connects to the device as soon as a service value is read/written. - // Thus we read one value in order to establish the connection. - for (uint i = 0; i < serviceCount; ++i) { - ComPtr service; - hr = deviceServices->GetAt(i, &service); - WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service"); - ComPtr service3; - hr = service.As(&service3); - WARN_AND_CONTINUE_IF_FAILED(hr, "Could not cast service"); - ComPtr> characteristicsOp; - hr = service3->GetCharacteristicsAsync(&characteristicsOp); - WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic"); - ComPtr characteristicsResult; - hr = QWinRTFunctions::await(characteristicsOp, characteristicsResult.GetAddressOf(), - QWinRTFunctions::ProcessMainThreadEvents, 5000); - WARN_AND_CONTINUE_IF_FAILED(hr, "Could not await characteristic operation"); - GattCommunicationStatus commStatus; - hr = characteristicsResult->get_Status(&commStatus); - if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) { - qCWarning(QT_BT_WINRT) << "Characteristic operation failed"; - continue; - } - ComPtr> characteristics; - hr = characteristicsResult->get_Characteristics(&characteristics); - if (hr == E_ACCESSDENIED) { - // Everything will work as expected up until this point if the manifest capabilties - // for bluetooth LE are not set. - qCWarning(QT_BT_WINRT) << "Could not obtain characteristic list. Please check your " - "manifest capabilities"; - setState(QLowEnergyController::UnconnectedState); - setError(QLowEnergyController::ConnectionError); - unregisterFromStatusChanges(); - return; - } - WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic list"); - uint characteristicsCount; - hr = characteristics->get_Size(&characteristicsCount); - WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic list's size"); - for (uint j = 0; j < characteristicsCount; ++j) { - ComPtr characteristic; - hr = characteristics->GetAt(j, &characteristic); - WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic"); - ComPtr> op; - GattCharacteristicProperties props; - hr = characteristic->get_CharacteristicProperties(&props); - WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic's properties"); - if (!(props & GattCharacteristicProperties_Read)) - continue; - hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode::BluetoothCacheMode_Uncached, &op); - WARN_AND_CONTINUE_IF_FAILED(hr, "Could not read characteristic value"); - ComPtr result; - hr = QWinRTFunctions::await(op, result.GetAddressOf()); - WARN_AND_CONTINUE_IF_FAILED(hr, "Could await characteristic read"); - ComPtr buffer; - hr = result->get_Value(&buffer); - WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic value"); - if (!buffer) { - qCDebug(QT_BT_WINRT) << "Problem reading value"; - continue; - } - return; - } - } - - qCWarning(QT_BT_WINRT) << "Could not obtain characteristic read result that triggers" - "device connection. Is the device reachable?"; - unregisterFromStatusChanges(); - setError(QLowEnergyController::ConnectionError); - setState(QLowEnergyController::UnconnectedState); + QBluetoothLocalDevice localDevice; + QBluetoothLocalDevice::Pairing pairing = localDevice.pairingStatus(remoteDevice); + if (pairing == QBluetoothLocalDevice::Unpaired) + connectToUnpairedDevice(); + else + connectToPairedDevice(); } void QLowEnergyControllerPrivateWinRTNew::disconnectFromDevice() @@ -594,6 +500,7 @@ void QLowEnergyControllerPrivateWinRTNew::disconnectFromDevice() setState(QLowEnergyController::ClosingState); unregisterFromValueChanges(); unregisterFromStatusChanges(); + mAbortPending = true; mDevice = nullptr; setState(QLowEnergyController::UnconnectedState); emit q->disconnected(); @@ -1614,6 +1521,160 @@ void QLowEnergyControllerPrivateWinRTNew::handleServiceHandlerError(const QStrin setError(QLowEnergyController::ConnectionError); } +void QLowEnergyControllerPrivateWinRTNew::connectToPairedDevice() +{ + Q_Q(QLowEnergyController); + ComPtr device3; + HRESULT hr = mDevice.As(&device3); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast device", return) + ComPtr> deviceServicesOp; + while (!mAbortPending) { + hr = device3->GetGattServicesAsync(&deviceServicesOp); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain services", return) + ComPtr deviceServicesResult; + hr = QWinRTFunctions::await(deviceServicesOp, deviceServicesResult.GetAddressOf(), + QWinRTFunctions::ProcessThreadEvents, 5000); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await services operation", return) + + GattCommunicationStatus commStatus; + hr = deviceServicesResult->get_Status(&commStatus); + if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) { + qCWarning(QT_BT_WINRT()) << "Service operation failed"; + setError(QLowEnergyController::ConnectionError); + setState(QLowEnergyController::UnconnectedState); + unregisterFromStatusChanges(); + return; + } + + ComPtr> deviceServices; + hr = deviceServicesResult->get_Services(&deviceServices); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain list of services", return) + uint serviceCount; + hr = deviceServices->get_Size(&serviceCount); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service count", return) + + if (serviceCount == 0) { + qCWarning(QT_BT_WINRT()) << "Found devices without services"; + setError(QLowEnergyController::ConnectionError); + setState(QLowEnergyController::UnconnectedState); + unregisterFromStatusChanges(); + return; + } + + // Windows automatically connects to the device as soon as a service value is read/written. + // Thus we read one value in order to establish the connection. + for (uint i = 0; i < serviceCount; ++i) { + ComPtr service; + hr = deviceServices->GetAt(i, &service); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service", return); + ComPtr service3; + hr = service.As(&service3); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast service", return); + ComPtr> characteristicsOp; + hr = service3->GetCharacteristicsAsync(&characteristicsOp); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic", return); + ComPtr characteristicsResult; + hr = QWinRTFunctions::await(characteristicsOp, characteristicsResult.GetAddressOf(), + QWinRTFunctions::ProcessThreadEvents, 5000); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await characteristic operation", return); + GattCommunicationStatus commStatus; + hr = characteristicsResult->get_Status(&commStatus); + if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) { + qCWarning(QT_BT_WINRT) << "Characteristic operation failed"; + break; + } + ComPtr> characteristics; + hr = characteristicsResult->get_Characteristics(&characteristics); + if (hr == E_ACCESSDENIED) { + // Everything will work as expected up until this point if the manifest capabilties + // for bluetooth LE are not set. + qCWarning(QT_BT_WINRT) << "Could not obtain characteristic list. Please check your " + "manifest capabilities"; + setState(QLowEnergyController::UnconnectedState); + setError(QLowEnergyController::ConnectionError); + unregisterFromStatusChanges(); + return; + } + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic list", return); + uint characteristicsCount; + hr = characteristics->get_Size(&characteristicsCount); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic list's size", return); + for (uint j = 0; j < characteristicsCount; ++j) { + ComPtr characteristic; + hr = characteristics->GetAt(j, &characteristic); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic", return); + ComPtr> op; + GattCharacteristicProperties props; + hr = characteristic->get_CharacteristicProperties(&props); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic's properties", return); + if (!(props & GattCharacteristicProperties_Read)) + continue; + hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode::BluetoothCacheMode_Uncached, &op); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not read characteristic value", return); + ComPtr result; + hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessThreadEvents, 500); + // E_ILLEGAL_METHOD_CALL will be the result for a device, that is not reachable at + // the moment. In this case we should jump back into the outer loop and keep trying. + if (hr == E_ILLEGAL_METHOD_CALL) + break; + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await characteristic read", return); + ComPtr buffer; + hr = result->get_Value(&buffer); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic value", return); + if (!buffer) { + qCDebug(QT_BT_WINRT) << "Problem reading value"; + break; + } + + setState(QLowEnergyController::ConnectedState); + emit q->connected(); + if (!registerForStatusChanges()) { + setError(QLowEnergyController::ConnectionError); + setState(QLowEnergyController::UnconnectedState); + return; + } + return; + } + } + } +} + +void QLowEnergyControllerPrivateWinRTNew::connectToUnpairedDevice() +{ + if (!registerForStatusChanges()) { + setError(QLowEnergyController::ConnectionError); + setState(QLowEnergyController::UnconnectedState); + return; + } + ComPtr device3; + HRESULT hr = mDevice.As(&device3); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast device", return) + ComPtr deviceServicesResult; + while (!mAbortPending) { + ComPtr> deviceServicesOp; + hr = device3->GetGattServicesAsync(&deviceServicesOp); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain services", return) + hr = QWinRTFunctions::await(deviceServicesOp, deviceServicesResult.GetAddressOf(), + QWinRTFunctions::ProcessMainThreadEvents); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await services operation", return) + + GattCommunicationStatus commStatus; + hr = deviceServicesResult->get_Status(&commStatus); + if (commStatus == GattCommunicationStatus_Unreachable) + continue; + + if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) { + qCWarning(QT_BT_WINRT()) << "Service operation failed"; + setError(QLowEnergyController::ConnectionError); + setState(QLowEnergyController::UnconnectedState); + unregisterFromStatusChanges(); + return; + } + + break; + } +} + QT_END_NAMESPACE #include "qlowenergycontroller_winrt_new.moc" diff --git a/src/bluetooth/qlowenergycontroller_winrt_new_p.h b/src/bluetooth/qlowenergycontroller_winrt_new_p.h index fa8e516f..adb7bbcb 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new_p.h +++ b/src/bluetooth/qlowenergycontroller_winrt_new_p.h @@ -122,6 +122,10 @@ private slots: void handleServiceHandlerError(const QString &error); private: + void connectToPairedDevice(); + void connectToUnpairedDevice(); + + bool mAbortPending = false; Microsoft::WRL::ComPtr mDevice; EventRegistrationToken mStatusChangedToken; struct ValueChangedEntry { -- cgit v1.2.3 From 80c84c4df0cb8dc5389542dde34317102f7b25b8 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Thu, 25 Apr 2019 10:43:59 +0200 Subject: qlowenergycontroller_winrt_new_p.h: Use forward declarations instead of includes when possible Change-Id: Ic995631dfc15e34c7f2902bfa850c97671c52367 Reviewed-by: Alex Blasche --- src/bluetooth/qlowenergycontroller_winrt_new.cpp | 1 + src/bluetooth/qlowenergycontroller_winrt_new_p.h | 26 ++++++++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp index 7bff47be..bb9894ff 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include diff --git a/src/bluetooth/qlowenergycontroller_winrt_new_p.h b/src/bluetooth/qlowenergycontroller_winrt_new_p.h index adb7bbcb..8cc5f9ce 100644 --- a/src/bluetooth/qlowenergycontroller_winrt_new_p.h +++ b/src/bluetooth/qlowenergycontroller_winrt_new_p.h @@ -60,9 +60,28 @@ #include "qlowenergycontroller.h" #include "qlowenergycontrollerbase_p.h" +namespace ABI { + namespace Windows { + namespace Devices { + namespace Bluetooth { + namespace GenericAttributeProfile { + class GattDeviceServicesResult; + struct IGattCharacteristic; + struct IGattDeviceService; + struct IGattValueChangedEventArgs; + } + + struct IBluetoothLEDevice; + } + } + namespace Foundation { + template struct IAsyncOperation; + enum class AsyncStatus; + } + } +} + #include -#include -#include #include @@ -78,7 +97,6 @@ QLowEnergyControllerPrivate *createWinRTLowEnergyController(); class QLowEnergyControllerPrivateWinRTNew final : public QLowEnergyControllerPrivate { - Q_OBJECT public: QLowEnergyControllerPrivateWinRTNew(); ~QLowEnergyControllerPrivateWinRTNew() override; @@ -157,7 +175,7 @@ private: void obtainIncludedServices(QSharedPointer servicePointer, Microsoft::WRL::ComPtr nativeService); HRESULT onServiceDiscoveryFinished(ABI::Windows::Foundation::IAsyncOperation *op, - AsyncStatus status); + ABI::Windows::Foundation::AsyncStatus status); }; QT_END_NAMESPACE -- cgit v1.2.3 From ac5200797be2dfb1f9fee9dafc3d1cb27c428ad2 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Thu, 25 Apr 2019 10:41:14 +0200 Subject: Fix nfc corkboard example on Android The example specified an Android SDK version to low for Qt's general min SDK version which was raised to v21 in Qt 5.13. Change-Id: I3555df2aee90bcda6e5493cd8bc10b6873279887 Reviewed-by: BogDan Vatra --- examples/nfc/corkboard/android/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/nfc/corkboard/android/AndroidManifest.xml b/examples/nfc/corkboard/android/AndroidManifest.xml index 68566b1b..ac0c5a67 100644 --- a/examples/nfc/corkboard/android/AndroidManifest.xml +++ b/examples/nfc/corkboard/android/AndroidManifest.xml @@ -72,7 +72,7 @@ - +