From 78149c7c26722c85b624dd4bc49942638e12b92c Mon Sep 17 00:00:00 2001 From: Ivan Solovev Date: Wed, 13 Jul 2022 12:09:11 +0200 Subject: Windows: refactor low energy device discovery This is the continuation of porting QWinRTBluetoothDiscoveryWorker to C++/WinRT. This allows to simplify the code and completely get rid of COM APIs in qbluetoothdevicediscoveryagent_winrt.cpp This patch is mostly a plain rewrite, with minimal logic changes. As with classic device discovery, we need to remember that callbacks from async operations come in separate threads. We also wrap BluetoothLEAdvertisementWatcher into a helper class and use signals to notify about new data. Task-number: QTBUG-103263 Change-Id: I3376930d145dccac2ded400e05c409f64fc24897 Reviewed-by: Juha Vuolle Reviewed-by: Oliver Wolff (cherry picked from commit 36dd802c964f97522d1f5a75c8fb7a67f3061a3d) --- .../qbluetoothdevicediscoveryagent_winrt.cpp | 884 ++++++++------------- 1 file changed, 315 insertions(+), 569 deletions(-) diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp index e3c51b91..263e1f13 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. @@ -47,63 +47,30 @@ #include #include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include - -#include - -#include #include +#include #include +#include +#include #include #include +#include -using WinRtBluetoothDevice = winrt::Windows::Devices::Bluetooth::BluetoothDevice; -using WinRtRfcommDeviceServicesResult = winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceServicesResult; -using WinRtAsyncOperation = winrt::Windows::Foundation::IAsyncOperation; -using WinRtAsyncStatus = winrt::Windows::Foundation::AsyncStatus; - -using namespace Microsoft::WRL; -using namespace Microsoft::WRL::Wrappers; -using namespace ABI::Windows::Foundation; -using namespace ABI::Windows::Foundation::Collections; -using namespace ABI::Windows::Devices; -using namespace ABI::Windows::Devices::Bluetooth; -using namespace ABI::Windows::Devices::Bluetooth::Advertisement; -using namespace ABI::Windows::Devices::Enumeration; -using namespace ABI::Windows::Storage::Streams; +using namespace winrt::Windows::Devices::Bluetooth; +using namespace winrt::Windows::Devices::Bluetooth::Advertisement; +using namespace winrt::Windows::Devices::Bluetooth::GenericAttributeProfile; +using namespace winrt::Windows::Devices::Bluetooth::Rfcomm; +using namespace winrt::Windows::Devices::Enumeration; +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Storage::Streams; QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS) -#define EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED(msg, error, ret) \ - if (FAILED(hr)) { \ - emit errorOccured(error); \ - qCWarning(QT_BT_WINDOWS) << msg; \ - ret; \ - } - -#define WARN_AND_RETURN_IF_FAILED(msg, ret) \ - if (FAILED(hr)) { \ - qCWarning(QT_BT_WINDOWS) << msg; \ - ret; \ - } - -#define WARN_AND_CONTINUE_IF_FAILED(msg) \ - if (FAILED(hr)) { \ - qCWarning(QT_BT_WINDOWS) << msg; \ - continue; \ - } - // Endianness conversion for quint128 doesn't exist in qtendian.h inline quint128 qbswap(const quint128 src) { @@ -113,26 +80,20 @@ inline quint128 qbswap(const quint128 src) return dst; } -static ManufacturerData extractManufacturerData(ComPtr ad) +static QByteArray byteArrayFromBuffer(const IBuffer &buffer) +{ + const uint8_t *data = buffer.data(); + return QByteArray(reinterpret_cast(data), + static_cast(buffer.Length())); +} + +static ManufacturerData extractManufacturerData(const BluetoothLEAdvertisement &ad) { ManufacturerData ret; - ComPtr> data; - HRESULT hr = ad->get_ManufacturerData(&data); - WARN_AND_RETURN_IF_FAILED("Could not obtain list of manufacturer data.", return ret); - quint32 size; - hr = data->get_Size(&size); - WARN_AND_RETURN_IF_FAILED("Could not obtain manufacturer data's list size.", return ret); - for (quint32 i = 0; i < size; ++i) { - ComPtr d; - hr = data->GetAt(i, &d); - WARN_AND_CONTINUE_IF_FAILED("Could not obtain manufacturer data."); - quint16 id; - hr = d->get_CompanyId(&id); - WARN_AND_CONTINUE_IF_FAILED("Could not obtain manufacturer data company id."); - ComPtr buffer; - hr = d->get_Data(&buffer); - WARN_AND_CONTINUE_IF_FAILED("Could not obtain manufacturer data set."); - const QByteArray bufferData = byteArrayFromBuffer(buffer); + const auto data = ad.ManufacturerData(); + for (const auto &item : data) { + const uint16_t id = item.CompanyId(); + const QByteArray bufferData = byteArrayFromBuffer(item.Data()); if (ret.contains(id)) qCWarning(QT_BT_WINDOWS) << "Company ID already present in manufacturer data."; ret.insert(id, bufferData); @@ -140,44 +101,27 @@ static ManufacturerData extractManufacturerData(ComPtr ad) +static ServiceData extractServiceData(const BluetoothLEAdvertisement &ad) { - ServiceData ret; + static constexpr int serviceDataTypes[3] = { 0x16, 0x20, 0x21 }; - int serviceDataTypes[3] = { 0x16, 0x20, 0x21 }; + ServiceData ret; for (const auto &serviceDataType : serviceDataTypes) { - ComPtr> data_sections; - HRESULT hr = ad->GetSectionsByType(serviceDataType, &data_sections); - WARN_AND_RETURN_IF_FAILED("Could not obtain list of advertisement data sections.", - return ret); - - quint32 size; - hr = data_sections->get_Size(&size); - WARN_AND_RETURN_IF_FAILED("Could not obtain advertisement data sections list size.", - return ret); - - for (quint32 i = 0; i < size; ++i) { - ComPtr d; - hr = data_sections->GetAt(i, &d); - WARN_AND_CONTINUE_IF_FAILED("Could not obtain service data."); - - BYTE datatype; - hr = d->get_DataType(&datatype); - WARN_AND_CONTINUE_IF_FAILED("Could not obtain service data type."); - - ComPtr buffer; - hr = d->get_Data(&buffer); - WARN_AND_CONTINUE_IF_FAILED("Could not obtain service data buffer."); - const QByteArray bufferData = byteArrayFromBuffer(buffer); - - if (datatype == 0x16) { + const auto dataSections = ad.GetSectionsByType(serviceDataType); + for (const auto §ion : dataSections) { + const unsigned char dataType = section.DataType(); + const QByteArray bufferData = byteArrayFromBuffer(section.Data()); + if (dataType == 0x16) { + Q_ASSERT(bufferData.size() >= 2); ret.insert(QBluetoothUuid(qFromLittleEndian(bufferData.constData())), bufferData + 2); - } else if (datatype == 0x20) { + } else if (dataType == 0x20) { + Q_ASSERT(bufferData.size() >= 4); ret.insert(QBluetoothUuid(qFromLittleEndian(bufferData.constData())), bufferData + 4); - } else if (datatype == 0x21) { + } else if (dataType == 0x21) { + Q_ASSERT(bufferData.size() >= 16); ret.insert(QBluetoothUuid(qToBigEndian( qFromLittleEndian(bufferData.constData()))), bufferData + 16); @@ -188,13 +132,99 @@ static ServiceData extractServiceData(ComPtr ad) return ret; } +// Needed because there is no explicit conversion +static GUID fromWinRtGuid(const winrt::guid &guid) +{ + const GUID uuid { + guid.Data1, + guid.Data2, + guid.Data3, + { guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], + guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7] } + }; + return uuid; +} + +class AdvertisementWatcherWrapper : public QObject, + public std::enable_shared_from_this +{ + Q_OBJECT +public: + AdvertisementWatcherWrapper() {} + ~AdvertisementWatcherWrapper() + { + unsubscribeFromEvents(); + stop(); + } + void init() + { + m_watcher.ScanningMode(BluetoothLEScanningMode::Active); + subscribeToEvents(); + } + void start() { m_watcher.Start(); } + void stop() + { + if (canStop()) + m_watcher.Stop(); + } + +signals: + // The signal will be emitted from a separate thread, + // so we need to use Qt::QueuedConnection + void advertisementDataReceived(quint64 address, qint16 rssi, + const ManufacturerData &manufacturerData, + const ServiceData &serviceData, + const QList &uuids); +private: + void subscribeToEvents() + { + // The callbacks are triggered from separate threads. So we capture + // thisPtr to make sure that the object is valid. + auto thisPtr = shared_from_this(); + m_receivedToken = m_watcher.Received( + [thisPtr](BluetoothLEAdvertisementWatcher, + BluetoothLEAdvertisementReceivedEventArgs args) { + const uint64_t address = args.BluetoothAddress(); + const short rssi = args.RawSignalStrengthInDBm(); + const BluetoothLEAdvertisement ad = args.Advertisement(); + + const ManufacturerData manufacturerData = extractManufacturerData(ad); + const ServiceData serviceData = extractServiceData(ad); + + QList serviceUuids; + const auto guids = ad.ServiceUuids(); + for (const auto &guid : guids) { + const GUID uuid = fromWinRtGuid(guid); + serviceUuids.append(QBluetoothUuid(uuid)); + } + + emit thisPtr->advertisementDataReceived(address, rssi, manufacturerData, + serviceData, serviceUuids); + }); + } + void unsubscribeFromEvents() + { + m_watcher.Received(m_receivedToken); + } + bool canStop() const + { + const auto status = m_watcher.Status(); + return status == BluetoothLEAdvertisementWatcherStatus::Started + || status == BluetoothLEAdvertisementWatcherStatus::Aborted; + } + + BluetoothLEAdvertisementWatcher m_watcher; + winrt::event_token m_receivedToken; +}; + // Both constants are taken from Microsoft's docs: // https://docs.microsoft.com/en-us/windows/uwp/devices-sensors/aep-service-class-ids // Alternatively we could create separate watchers for paired and unpaired devices. static const winrt::hstring ClassicDeviceSelector = L"System.Devices.Aep.ProtocolId:=\"{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}\""; -static const winrt::hstring LowEnergyDeviceSelector = - L"System.Devices.Aep.ProtocolId:=\"{bb7bb05e-5972-42b5-94fc-76eaa7084d49}\""; +// Do not use it for now, so comment out. Do not delete in case we want to reuse it. +//static const winrt::hstring LowEnergyDeviceSelector = +// L"System.Devices.Aep.ProtocolId:=\"{bb7bb05e-5972-42b5-94fc-76eaa7084d49}\""; class QWinRTBluetoothDeviceDiscoveryWorker : public QObject, public std::enable_shared_from_this @@ -208,36 +238,27 @@ public: private: void startDeviceDiscovery(QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode); - void onDeviceDiscoveryFinished(IAsyncOperation *op, - QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode); - void gatherDeviceInformation(IDeviceInformation *deviceInfo, - QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode); - void gatherMultipleDeviceInformation(quint32 deviceCount, IVectorView *devices, - QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode); - void setupLEDeviceWatcher(); - void leBluetoothInfoFromDeviceIdAsync(HSTRING deviceId); - void leBluetoothInfoFromAddressAsync(quint64 address); - HRESULT onPairedBluetoothLEDeviceFoundAsync(IAsyncOperation *op, AsyncStatus status); - HRESULT onBluetoothLEDeviceFoundAsync(IAsyncOperation *op, AsyncStatus status); - enum PairingCheck { - CheckForPairing, - OmitPairingCheck - }; - HRESULT onBluetoothLEDeviceFound(ComPtr device, PairingCheck pairingCheck); - HRESULT onBluetoothLEDeviceFound(ComPtr device); - HRESULT onBluetoothLEAdvertisementReceived(IBluetoothLEAdvertisementReceivedEventArgs *args); - HRESULT onLeServicesReceived(IAsyncOperation *op, - AsyncStatus status, QBluetoothDeviceInfo &info); std::shared_ptr createDeviceWatcher(winrt::hstring selector, int watcherId); void generateError(QBluetoothDeviceDiscoveryAgent::Error error, const char *msg = nullptr); + void invokeDeviceFoundWithDebug(const QBluetoothDeviceInfo &info); + // Bluetooth Classic handlers void getClassicDeviceFromId(const winrt::hstring &id); - void handleClassicDevice(const WinRtBluetoothDevice &device); - void handleRfcommServices(const WinRtRfcommDeviceServicesResult &servicesResult, + void handleClassicDevice(const BluetoothDevice &device); + void handleRfcommServices(const RfcommDeviceServicesResult &servicesResult, uint64_t address, const QString &name, uint32_t classOfDeviceInt); + // Bluetooth Low Energy handlers + void getLowEnergyDeviceFromId(const winrt::hstring &id); + void handleLowEnergyDevice(const BluetoothLEDevice &device); + void handleGattServices(const GattDeviceServicesResult &servicesResult, + QBluetoothDeviceInfo &info); + + // Bluetooth Low Energy Advertising handlers + std::shared_ptr createAdvertisementWatcher(); + // invokable methods for handling finish conditions Q_INVOKABLE void incrementPendingDevicesCount(); Q_INVOKABLE void decrementPendingDevicesCountAndCheckFinished(); @@ -252,17 +273,16 @@ Q_SIGNALS: void errorOccured(QBluetoothDeviceDiscoveryAgent::Error error); void scanFinished(); -public: - quint8 requestedModes = 0; - private slots: void onBluetoothDeviceFound(winrt::hstring deviceId, int watcherId); void onDeviceEnumerationCompleted(int watcherId); + void onAdvertisementDataReceived(quint64 address, qint16 rssi, + const ManufacturerData &manufacturerData, + const ServiceData &serviceData, + const QList &uuids); + private: - ComPtr m_leWatcher; - EventRegistrationToken m_leDeviceAddedToken; - QMutex m_foundDevicesMutex; struct LEAdvertisingInfo { QList services; ManufacturerData manufacturerData; @@ -270,16 +290,17 @@ private: qint16 rssi = 0; }; + quint8 requestedModes = 0; + QMutex m_leDevicesMutex; QMap m_foundLEDevicesMap; - int m_pendingPairedDevices = 0; - - ComPtr m_leDeviceStatics; + int m_pendingDevices = 0; static constexpr int ClassicWatcherId = 1; static constexpr int LowEnergyWatcherId = 2; std::shared_ptr m_classicWatcher; std::shared_ptr m_lowEnergyWatcher; + std::shared_ptr m_advertisementWatcher; bool m_classicScanStarted = false; bool m_lowEnergyScanStarted = false; }; @@ -302,11 +323,11 @@ QWinRTBluetoothDeviceDiscoveryWorker::QWinRTBluetoothDeviceDiscoveryWorker() qRegisterMetaType(); m_classicWatcher = createDeviceWatcher(ClassicDeviceSelector, ClassicWatcherId); - - HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice).Get(), &m_leDeviceStatics); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain bluetooth le device factory", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return) + // For LE scan use DeviceWatcher to handle only paired devices. + // Non-paired devices will be found using BluetoothLEAdvertisementWatcher. + const auto leSelector = BluetoothLEDevice::GetDeviceSelectorFromPairingState(true); + m_lowEnergyWatcher = createDeviceWatcher(leSelector, LowEnergyWatcherId); + m_advertisementWatcher = createAdvertisementWatcher(); } QWinRTBluetoothDeviceDiscoveryWorker::~QWinRTBluetoothDeviceDiscoveryWorker() @@ -318,11 +339,6 @@ void QWinRTBluetoothDeviceDiscoveryWorker::start(QBluetoothDeviceDiscoveryAgent: { requestedModes = methods; - if (requestedModes & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod) { - startDeviceDiscovery(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod); - setupLEDeviceWatcher(); - } - if (requestedModes & QBluetoothDeviceDiscoveryAgent::ClassicMethod) { if (m_classicWatcher && m_classicWatcher->init()) { m_classicWatcher->start(); @@ -330,10 +346,24 @@ void QWinRTBluetoothDeviceDiscoveryWorker::start(QBluetoothDeviceDiscoveryAgent: } else { generateError(QBluetoothDeviceDiscoveryAgent::Error::UnknownError, "Could not start classic device watcher"); - // do not return here, because we might still start LE scan } } - // TODO - do the same for LE scan + if (requestedModes & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod) { + if (m_lowEnergyWatcher && m_lowEnergyWatcher->init()) { + m_lowEnergyWatcher->start(); + m_lowEnergyScanStarted = true; + } else { + generateError(QBluetoothDeviceDiscoveryAgent::Error::UnknownError, + "Could not start low energy device watcher"); + } + if (m_advertisementWatcher) { + m_advertisementWatcher->init(); + m_advertisementWatcher->start(); + } else { + generateError(QBluetoothDeviceDiscoveryAgent::Error::UnknownError, + "Could not start low energy advertisement watcher"); + } + } qCDebug(QT_BT_WINDOWS) << "Worker started"; } @@ -341,141 +371,52 @@ void QWinRTBluetoothDeviceDiscoveryWorker::start(QBluetoothDeviceDiscoveryAgent: void QWinRTBluetoothDeviceDiscoveryWorker::stop() { m_classicWatcher->stop(); - if (m_leWatcher) { - HRESULT hr = m_leWatcher->Stop(); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not stop le watcher", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return) - if (m_leDeviceAddedToken.value) { - hr = m_leWatcher->remove_Received(m_leDeviceAddedToken); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could remove le watcher token", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return) - } - } -} - -void QWinRTBluetoothDeviceDiscoveryWorker::startDeviceDiscovery(QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode) -{ - HString deviceSelector; - ComPtr deviceInformationStatics; - HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation).Get(), &deviceInformationStatics); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain device information statics", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return); - if (mode == QBluetoothDeviceDiscoveryAgent::LowEnergyMethod) - m_leDeviceStatics->GetDeviceSelector(deviceSelector.GetAddressOf()); - else - return; // Classic scan is now implemented using C++/WinRT DeviceWatcher - - ComPtr> op; - hr = deviceInformationStatics->FindAllAsyncAqsFilter(deviceSelector.Get(), &op); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not start bluetooth device discovery operation", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return); - QPointer thisPointer(this); - hr = op->put_Completed( - Callback>([thisPointer, mode](IAsyncOperation *op, AsyncStatus status) { - if (status == Completed && thisPointer) - thisPointer->onDeviceDiscoveryFinished(op, mode); - return S_OK; - }).Get()); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not add device discovery callback", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return); + m_lowEnergyWatcher->stop(); + m_advertisementWatcher->stop(); } -void QWinRTBluetoothDeviceDiscoveryWorker::onDeviceDiscoveryFinished(IAsyncOperation *op, QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode) +void QWinRTBluetoothDeviceDiscoveryWorker::finishDiscovery() { - qCDebug(QT_BT_WINDOWS) << (mode == QBluetoothDeviceDiscoveryAgent::ClassicMethod ? "BT" : "BTLE") - << "scan completed"; - ComPtr> devices; - HRESULT hr; - hr = op->GetResults(&devices); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain discovery result", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return); - quint32 deviceCount; - hr = devices->get_Size(&deviceCount); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain discovery result size", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return); - - m_pendingPairedDevices += deviceCount; - gatherMultipleDeviceInformation(deviceCount, devices.Get(), mode); + stop(); + emit scanFinished(); } -void QWinRTBluetoothDeviceDiscoveryWorker::gatherDeviceInformation(IDeviceInformation *deviceInfo, QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode) +void QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothDeviceFound(winrt::hstring deviceId, int watcherId) { - HString deviceId; - HRESULT hr; - hr = deviceInfo->get_Id(deviceId.GetAddressOf()); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain device ID", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return); - if (mode == QBluetoothDeviceDiscoveryAgent::LowEnergyMethod) - leBluetoothInfoFromDeviceIdAsync(deviceId.Get()); + if (watcherId == ClassicWatcherId) + getClassicDeviceFromId(deviceId); + else if (watcherId == LowEnergyWatcherId) + getLowEnergyDeviceFromId(deviceId); } -void QWinRTBluetoothDeviceDiscoveryWorker::gatherMultipleDeviceInformation(quint32 deviceCount, IVectorView *devices, QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode) +void QWinRTBluetoothDeviceDiscoveryWorker::onDeviceEnumerationCompleted(int watcherId) { - for (quint32 i = 0; i < deviceCount; ++i) { - ComPtr device; - HRESULT hr; - hr = devices->GetAt(i, &device); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain device", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return); - gatherDeviceInformation(device.Get(), mode); + qCDebug(QT_BT_WINDOWS) << (watcherId == ClassicWatcherId ? "BT" : "BTLE") + << "enumeration completed"; + if (watcherId == ClassicWatcherId) { + m_classicWatcher->stop(); + m_classicScanStarted = false; + } else if (watcherId == LowEnergyWatcherId) { + m_lowEnergyWatcher->stop(); + m_lowEnergyScanStarted = false; + } + // TODO - probably reconsider this condition later + if (!m_lowEnergyScanStarted && !m_classicScanStarted && !m_pendingDevices + && !(requestedModes & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod)) { + finishDiscovery(); } } -HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEAdvertisementReceived(IBluetoothLEAdvertisementReceivedEventArgs *args) +// this function executes in main worker thread +void QWinRTBluetoothDeviceDiscoveryWorker::onAdvertisementDataReceived( + quint64 address, qint16 rssi, const ManufacturerData &manufacturerData, + const ServiceData &serviceData, const QList &uuids) { - quint64 address; - HRESULT hr; - hr = args->get_BluetoothAddress(&address); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain bluetooth address", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - qint16 rssi; - hr = args->get_RawSignalStrengthInDBm(&rssi); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain signal strength", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - ComPtr ad; - hr = args->get_Advertisement(&ad); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could get advertisement", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - const ManufacturerData manufacturerData = extractManufacturerData(ad); - const ServiceData serviceData = extractServiceData(ad); - QBluetoothDeviceInfo::Fields changedFields = QBluetoothDeviceInfo::Field::None; - ComPtr> guids; - hr = ad->get_ServiceUuids(&guids); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain service uuid list", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - quint32 size; - hr = guids->get_Size(&size); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain service uuid list size", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - QList serviceUuids; - for (quint32 i = 0; i < size; ++i) { - GUID guid; - hr = guids->GetAt(i, &guid); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain uuid", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - QBluetoothUuid uuid(guid); - serviceUuids.append(uuid); - } - - { // scope for QMutexLocker - QMutexLocker locker(&m_foundDevicesMutex); - // Merge newly found services with list of currently found ones + // Merge newly found services with list of currently found ones + { + QMutexLocker locker(&m_leDevicesMutex); if (m_foundLEDevicesMap.contains(address)) { + QBluetoothDeviceInfo::Fields changedFields = QBluetoothDeviceInfo::Field::None; const LEAdvertisingInfo adInfo = m_foundLEDevicesMap.value(address); QList foundServices = adInfo.services; if (adInfo.rssi != rssi) { @@ -493,7 +434,7 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEAdvertisementReceived changedFields.setFlag((QBluetoothDeviceInfo::Field::ServiceData)); } bool newServiceAdded = false; - for (const QBluetoothUuid &uuid : qAsConst(serviceUuids)) { + for (const QBluetoothUuid &uuid : qAsConst(uuids)) { if (!foundServices.contains(uuid)) { foundServices.append(uuid); newServiceAdded = true; @@ -508,87 +449,42 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEAdvertisementReceived Q_ARG(ManufacturerData, manufacturerData), Q_ARG(ServiceData, serviceData)); } - return S_OK; } m_foundLEDevicesMap[address].services = foundServices; } else { LEAdvertisingInfo info; - info.services = std::move(serviceUuids); + info.services = std::move(uuids); info.manufacturerData = std::move(manufacturerData); info.serviceData = std::move(serviceData); info.rssi = rssi; m_foundLEDevicesMap.insert(address, info); } } - leBluetoothInfoFromAddressAsync(address); - return S_OK; -} - -void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher() -{ - HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisementWatcher).Get(), &m_leWatcher); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not create advertisment watcher", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return); - hr = m_leWatcher->put_ScanningMode(BluetoothLEScanningMode_Active); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not set scanning mode", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return); - QPointer thisPointer(this); - hr = m_leWatcher->add_Received( - Callback>( - [thisPointer](IBluetoothLEAdvertisementWatcher *, IBluetoothLEAdvertisementReceivedEventArgs *args) { - if (thisPointer) - return thisPointer->onBluetoothLEAdvertisementReceived(args); - - return S_OK; - }).Get(), &m_leDeviceAddedToken); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not add device callback", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return); - hr = m_leWatcher->Start(); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not start device watcher", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return); -} - -void QWinRTBluetoothDeviceDiscoveryWorker::finishDiscovery() -{ - stop(); - emit scanFinished(); -} - -void QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothDeviceFound(winrt::hstring deviceId, int watcherId) -{ - if (watcherId == ClassicWatcherId) - getClassicDeviceFromId(deviceId); - // TODO - handle LE device -} - -void QWinRTBluetoothDeviceDiscoveryWorker::onDeviceEnumerationCompleted(int watcherId) -{ - qCDebug(QT_BT_WINDOWS) << (watcherId == ClassicWatcherId ? "BT" : "BTLE") - << "enumeration completed"; - if (watcherId == ClassicWatcherId) { - m_classicWatcher->stop(); - m_classicScanStarted = false; - } else if (watcherId == LowEnergyWatcherId) { - m_lowEnergyWatcher->stop(); - m_lowEnergyScanStarted = false; - } - // TODO - probably reconsider this condition later - if (!m_lowEnergyScanStarted && !m_classicScanStarted && !m_pendingPairedDevices - && !(requestedModes & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod)) { - finishDiscovery(); - } + invokeIncrementPendingDevicesCount(this); // as if we discovered a new LE device + auto thisPtr = shared_from_this(); + auto asyncOp = BluetoothLEDevice::FromBluetoothAddressAsync(address); + asyncOp.Completed([thisPtr, address](auto &&op, AsyncStatus status) { + if (thisPtr) { + if (status == AsyncStatus::Completed) { + BluetoothLEDevice device = op.GetResults(); + if (device) { + thisPtr->handleLowEnergyDevice(device); + return; + } + } + // status != Completed or failed to extract result + qCDebug(QT_BT_WINDOWS) << "Failed to get LE device from address" + << QBluetoothAddress(address); + invokeDecrementPendingDevicesCountAndCheckFinished(thisPtr.get()); + } + }); } std::shared_ptr QWinRTBluetoothDeviceDiscoveryWorker::createDeviceWatcher(winrt::hstring selector, int watcherId) { auto watcher = std::make_shared( - watcherId, selector, - winrt::Windows::Devices::Enumeration::DeviceInformationKind::AssociationEndpoint); + watcherId, selector, DeviceInformationKind::AssociationEndpoint); if (watcher) { connect(watcher.get(), &QBluetoothDeviceWatcherWinRT::deviceAdded, this, &QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothDeviceFound, @@ -607,16 +503,27 @@ void QWinRTBluetoothDeviceDiscoveryWorker::generateError( qCWarning(QT_BT_WINDOWS) << msg; } +void QWinRTBluetoothDeviceDiscoveryWorker::invokeDeviceFoundWithDebug(const QBluetoothDeviceInfo &info) +{ + qCDebug(QT_BT_WINDOWS) << "Discovered BTLE device: " << info.address() << info.name() + << "Num UUIDs" << info.serviceUuids().size() << "RSSI:" << info.rssi() + << "Num manufacturer data" << info.manufacturerData().size() + << "Num service data" << info.serviceData().size(); + + QMetaObject::invokeMethod(this, "deviceFound", Qt::AutoConnection, + Q_ARG(QBluetoothDeviceInfo, info)); +} + // this function executes in main worker thread void QWinRTBluetoothDeviceDiscoveryWorker::getClassicDeviceFromId(const winrt::hstring &id) { - ++m_pendingPairedDevices; - auto asyncOp = WinRtBluetoothDevice::FromIdAsync(id); + ++m_pendingDevices; auto thisPtr = shared_from_this(); - asyncOp.Completed([thisPtr](auto &&op, WinRtAsyncStatus status) { + auto asyncOp = BluetoothDevice::FromIdAsync(id); + asyncOp.Completed([thisPtr](auto &&op, AsyncStatus status) { if (thisPtr) { - if (status == WinRtAsyncStatus::Completed) { - WinRtBluetoothDevice device = op.GetResults(); + if (status == AsyncStatus::Completed) { + BluetoothDevice device = op.GetResults(); if (device) { thisPtr->handleClassicDevice(device); return; @@ -628,9 +535,9 @@ void QWinRTBluetoothDeviceDiscoveryWorker::getClassicDeviceFromId(const winrt::h } }); } -\ + // this is a callback - executes in a new thread -void QWinRTBluetoothDeviceDiscoveryWorker::handleClassicDevice(const WinRtBluetoothDevice &device) +void QWinRTBluetoothDeviceDiscoveryWorker::handleClassicDevice(const BluetoothDevice &device) { const uint64_t address = device.BluetoothAddress(); const std::wstring name { device.Name() }; // via operator std::wstring_view() @@ -638,9 +545,9 @@ void QWinRTBluetoothDeviceDiscoveryWorker::handleClassicDevice(const WinRtBlueto const uint32_t deviceClass = device.ClassOfDevice().RawValue(); auto thisPtr = shared_from_this(); auto asyncOp = device.GetRfcommServicesAsync(); - asyncOp.Completed([thisPtr, address, btName, deviceClass](auto &&op, WinRtAsyncStatus status) { + asyncOp.Completed([thisPtr, address, btName, deviceClass](auto &&op, AsyncStatus status) { if (thisPtr) { - if (status == WinRtAsyncStatus::Completed) { + if (status == AsyncStatus::Completed) { auto servicesResult = op.GetResults(); if (servicesResult) { thisPtr->handleRfcommServices(servicesResult, address, btName, deviceClass); @@ -656,7 +563,7 @@ void QWinRTBluetoothDeviceDiscoveryWorker::handleClassicDevice(const WinRtBlueto // this is a callback - executes in a new thread void QWinRTBluetoothDeviceDiscoveryWorker::handleRfcommServices( - const WinRtRfcommDeviceServicesResult &servicesResult, uint64_t address, + const RfcommDeviceServicesResult &servicesResult, uint64_t address, const QString &name, uint32_t classOfDeviceInt) { // need to perform the check even if some of the operations fails @@ -666,7 +573,7 @@ void QWinRTBluetoothDeviceDiscoveryWorker::handleRfcommServices( Q_UNUSED(guard); // to suppress warning const auto error = servicesResult.Error(); - if (error != winrt::Windows::Devices::Bluetooth::BluetoothError::Success) { + if (error != BluetoothError::Success) { qCWarning(QT_BT_WINDOWS) << "Obtain device services completed with BluetoothError" << static_cast(error); return; @@ -676,16 +583,7 @@ void QWinRTBluetoothDeviceDiscoveryWorker::handleRfcommServices( QList uuids; for (const auto &service : services) { const auto serviceId = service.ServiceId(); - const winrt::guid serviceUuid = serviceId.Uuid(); - // A cast from winrt::guid does not work :( - const GUID uuid { - serviceUuid.Data1, - serviceUuid.Data2, - serviceUuid.Data3, - { serviceUuid.Data4[0], serviceUuid.Data4[1], serviceUuid.Data4[2], - serviceUuid.Data4[3], serviceUuid.Data4[4], serviceUuid.Data4[5], - serviceUuid.Data4[6], serviceUuid.Data4[7] } - }; + const GUID uuid = fromWinRtGuid(serviceId.Uuid()); uuids.append(QBluetoothUuid(uuid)); } @@ -705,171 +603,50 @@ void QWinRTBluetoothDeviceDiscoveryWorker::handleRfcommServices( void QWinRTBluetoothDeviceDiscoveryWorker::incrementPendingDevicesCount() { - ++m_pendingPairedDevices; + ++m_pendingDevices; } void QWinRTBluetoothDeviceDiscoveryWorker::decrementPendingDevicesCountAndCheckFinished() { - if ((--m_pendingPairedDevices == 0) && !m_classicScanStarted && !m_lowEnergyScanStarted + if ((--m_pendingDevices == 0) && !m_classicScanStarted && !m_lowEnergyScanStarted && !(requestedModes & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod)) { finishDiscovery(); } } -// "deviceFound" will be emitted at the end of the deviceFromIdOperation callback -void QWinRTBluetoothDeviceDiscoveryWorker::leBluetoothInfoFromDeviceIdAsync(HSTRING deviceId) +// this function executes in main worker thread +void QWinRTBluetoothDeviceDiscoveryWorker::getLowEnergyDeviceFromId(const winrt::hstring &id) { - // Note: in this method we do not need to call - // decrementPairedDevicesAndCheckFinished() because we *do* run LE - // scanning, so the condition in the check will always be false. - // It's enough to just decrement m_pendingPairedDevices. - ComPtr> deviceFromIdOperation; - // on Windows 10 FromIdAsync might ask for device permission. We cannot wait here but have to handle that asynchronously - HRESULT hr = m_leDeviceStatics->FromIdAsync(deviceId, &deviceFromIdOperation); - if (FAILED(hr)) { - emit errorOccured(QBluetoothDeviceDiscoveryAgent::UnknownError); - --m_pendingPairedDevices; - qCWarning(QT_BT_WINDOWS) << "Could not obtain bluetooth device from id"; - return; - } - QPointer thisPointer(this); - hr = deviceFromIdOperation->put_Completed(Callback> - ([thisPointer] (IAsyncOperation *op, AsyncStatus status) - { - if (thisPointer) { - if (status == Completed) - thisPointer->onPairedBluetoothLEDeviceFoundAsync(op, status); - else - --thisPointer->m_pendingPairedDevices; + ++m_pendingDevices; + auto asyncOp = BluetoothLEDevice::FromIdAsync(id); + auto thisPtr = shared_from_this(); + asyncOp.Completed([thisPtr](auto &&op, AsyncStatus status) { + if (thisPtr) { + if (status == AsyncStatus::Completed) { + BluetoothLEDevice device = op.GetResults(); + if (device) { + thisPtr->handleLowEnergyDevice(device); + return; + } + } + // status != Completed or failed to extract result + qCDebug(QT_BT_WINDOWS) << "Failed to get LE device from id"; + invokeDecrementPendingDevicesCountAndCheckFinished(thisPtr.get()); } - return S_OK; - }).Get()); - if (FAILED(hr)) { - emit errorOccured(QBluetoothDeviceDiscoveryAgent::UnknownError); - --m_pendingPairedDevices; - qCWarning(QT_BT_WINDOWS) << "Could not register device found callback"; - return; - } -} - -// "deviceFound" will be emitted at the end of the deviceFromAdressOperation callback -void QWinRTBluetoothDeviceDiscoveryWorker::leBluetoothInfoFromAddressAsync(quint64 address) -{ - ComPtr> deviceFromAddressOperation; - // on Windows 10 FromBluetoothAddressAsync might ask for device permission. We cannot wait - // here but have to handle that asynchronously - HRESULT hr = m_leDeviceStatics->FromBluetoothAddressAsync(address, &deviceFromAddressOperation); - if (FAILED(hr)) { - emit errorOccured(QBluetoothDeviceDiscoveryAgent::UnknownError); - qCWarning(QT_BT_WINDOWS) << "Could not obtain bluetooth device from address"; - return; - } - QPointer thisPointer(this); - hr = deviceFromAddressOperation->put_Completed(Callback> - ([thisPointer](IAsyncOperation *op, AsyncStatus status) - { - if (status == Completed && thisPointer) - thisPointer->onBluetoothLEDeviceFoundAsync(op, status); - return S_OK; - }).Get()); - if (FAILED(hr)) { - emit errorOccured(QBluetoothDeviceDiscoveryAgent::UnknownError); - qCWarning(QT_BT_WINDOWS) << "Could not register device found callback"; - return; - } -} - -HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onPairedBluetoothLEDeviceFoundAsync(IAsyncOperation *op, AsyncStatus status) -{ - --m_pendingPairedDevices; - if (status != AsyncStatus::Completed) - return S_OK; - - ComPtr device; - HRESULT hr; - hr = op->GetResults(&device); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain bluetooth le device", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - return onBluetoothLEDeviceFound(device); -} - -HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFoundAsync(IAsyncOperation *op, AsyncStatus status) -{ - if (status != AsyncStatus::Completed) - return S_OK; - - ComPtr device; - HRESULT hr; - hr = op->GetResults(&device); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain bluetooth le device", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - return onBluetoothLEDeviceFound(device); -} - -static void invokeDeviceFoundWithDebug(QWinRTBluetoothDeviceDiscoveryWorker *worker, - const QBluetoothDeviceInfo &info) -{ - qCDebug(QT_BT_WINDOWS) << "Discovered BTLE device: " << info.address() << info.name() - << "Num UUIDs" << info.serviceUuids().size() << "RSSI:" << info.rssi() - << "Num manufacturer data" << info.manufacturerData().size() - << "Num service data" << info.serviceData().size(); - - QMetaObject::invokeMethod(worker, "deviceFound", Qt::AutoConnection, - Q_ARG(QBluetoothDeviceInfo, info)); + }); } -HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr device) +// this is a callback - executes in a new thread +void QWinRTBluetoothDeviceDiscoveryWorker::handleLowEnergyDevice(const BluetoothLEDevice &device) { - if (!device) { - qCDebug(QT_BT_WINDOWS) << "onBluetoothLEDeviceFound: No device given"; - return S_OK; - } - - UINT64 address; - HString name; - HRESULT hr = device->get_BluetoothAddress(&address); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain bluetooth address", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - hr = device->get_Name(name.GetAddressOf()); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain device name", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - const QString btName = QString::fromWCharArray(WindowsGetStringRawBuffer(name.Get(), nullptr)); - - ComPtr device2; - hr = device.As(&device2); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not cast device", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - ComPtr deviceInfo; - hr = device2->get_DeviceInformation(&deviceInfo); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain device info", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - if (!deviceInfo) { - qCDebug(QT_BT_WINDOWS) << "onBluetoothLEDeviceFound: Could not obtain device information"; - return S_OK; - } - ComPtr deviceInfo2; - hr = deviceInfo.As(&deviceInfo2); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain cast device info", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - ComPtr pairing; - hr = deviceInfo2->get_Pairing(&pairing); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain pairing information", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - boolean isPaired; - hr = pairing->get_IsPaired(&isPaired); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain pairing status", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); + const uint64_t address = device.BluetoothAddress(); + const std::wstring name { device.Name() }; // via operator std::wstring_view() + const QString btName = QString::fromStdWString(name); + const bool isPaired = device.DeviceInformation().Pairing().IsPaired(); + m_leDevicesMutex.lock(); const LEAdvertisingInfo adInfo = m_foundLEDevicesMap.value(address); + m_leDevicesMutex.unlock(); const ManufacturerData manufacturerData = adInfo.manufacturerData; const ServiceData serviceData = adInfo.serviceData; const qint16 rssi = adInfo.rssi; @@ -886,93 +663,62 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr device3; - hr = device.As(&device3); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Failed to obtain IBluetoothLEDevice3 instance", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - - ComPtr> servicesOp; - hr = device3->GetGattServicesAsync(&servicesOp); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Failed to execute async services request", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - - QPointer thisPtr(this); - hr = servicesOp->put_Completed( - Callback>([thisPtr, info]( - IAsyncOperation *op, - AsyncStatus status) mutable { - if (thisPtr) - thisPtr->onLeServicesReceived(op, status, info); - return S_OK; - }).Get()); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not add LE services discovery callback", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); + auto asyncOp = device.GetGattServicesAsync(); + auto thisPtr = shared_from_this(); + asyncOp.Completed([thisPtr, info](auto &&op, AsyncStatus status) mutable { + if (status == AsyncStatus::Completed) { + auto servicesResult = op.GetResults(); + if (servicesResult) { + thisPtr->handleGattServices(servicesResult, info); + return; + } + } + // Failed to get services + qCDebug(QT_BT_WINDOWS) << "Failed to get GATT services for device" << info.name(); + invokeDecrementPendingDevicesCountAndCheckFinished(thisPtr.get()); + }); } - - return S_OK; } -HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onLeServicesReceived( - IAsyncOperation *op, - AsyncStatus status, QBluetoothDeviceInfo &info) +// this is a callback - executes in a new thread +void QWinRTBluetoothDeviceDiscoveryWorker::handleGattServices( + const GattDeviceServicesResult &servicesResult, QBluetoothDeviceInfo &info) { - if (status != AsyncStatus::Completed) { - qCWarning(QT_BT_WINDOWS) << "LE service request finished with status" - << static_cast(status); - return S_OK; - } + // need to perform the check even if some of the operations fails + auto guard = qScopeGuard([this]() { + invokeDecrementPendingDevicesCountAndCheckFinished(this); + }); + Q_UNUSED(guard); // to suppress warning - ComPtr servicesResult; - HRESULT hr = op->GetResults(&servicesResult); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not get async operation result for LE services", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - - GenericAttributeProfile::GattCommunicationStatus commStatus; - hr = servicesResult->get_Status(&commStatus); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain services status", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - - if (commStatus == GenericAttributeProfile::GattCommunicationStatus_Success) { - IVectorView *deviceServices; - hr = servicesResult->get_Services(&deviceServices); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain gatt service list", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - uint serviceCount; - hr = deviceServices->get_Size(&serviceCount); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain gatt service list size", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); + const auto status = servicesResult.Status(); + if (status == GattCommunicationStatus::Success) { + const auto services = servicesResult.Services(); QList uuids; - for (uint i = 0; i < serviceCount; ++i) { - ComPtr service; - hr = deviceServices->GetAt(i, &service); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain gatt service", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); - GUID uuid; - hr = service->get_Uuid(&uuid); - EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not obtain uuid", - QBluetoothDeviceDiscoveryAgent::Error::UnknownError, - return S_OK); + for (const auto &service : services) { + const GUID uuid = fromWinRtGuid(service.Uuid()); uuids.append(QBluetoothUuid(uuid)); } info.setServiceUuids(uuids); } else { qCWarning(QT_BT_WINDOWS) << "Obtaining LE services finished with status" - << static_cast(commStatus); + << static_cast(status); } - invokeDeviceFoundWithDebug(this, info); + invokeDeviceFoundWithDebug(info); +} - return S_OK; +std::shared_ptr +QWinRTBluetoothDeviceDiscoveryWorker::createAdvertisementWatcher() +{ + auto watcher = std::make_shared(); + if (watcher) { + connect(watcher.get(), &AdvertisementWatcherWrapper::advertisementDataReceived, + this, &QWinRTBluetoothDeviceDiscoveryWorker::onAdvertisementDataReceived, + Qt::QueuedConnection); + } + return watcher; } QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate( -- cgit v1.2.1