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 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