summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Solovev <ivan.solovev@qt.io>2022-07-13 12:09:11 +0200
committerIvan Solovev <ivan.solovev@qt.io>2022-07-19 10:41:20 +0200
commit78149c7c26722c85b624dd4bc49942638e12b92c (patch)
tree4b726c2787f51289bc6b485674f4b8343bc53635
parent3380922779553d0063df7b639b8895b6eeae7999 (diff)
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 <juha.vuolle@insta.fi> Reviewed-by: Oliver Wolff <oliver.wolff@qt.io> (cherry picked from commit 36dd802c964f97522d1f5a75c8fb7a67f3061a3d)
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp884
1 files 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 <QtBluetooth/private/qtbluetoothglobal_p.h>
#include <QtCore/QLoggingCategory>
-#include <QtCore/qmutex.h>
-#include <QtCore/private/qfunctions_winrt_p.h>
+#include <QtCore/QMutex>
#include <QtCore/qendian.h>
-#include <robuffer.h>
-#include <wrl.h>
-#include <windows.devices.enumeration.h>
-#include <windows.devices.bluetooth.h>
-#include <windows.foundation.collections.h>
-#include <windows.storage.streams.h>
-
-#include <windows.devices.bluetooth.advertisement.h>
-
-#include <winrt/Windows.Devices.Enumeration.h>
#include <winrt/Windows.Devices.Bluetooth.h>
+#include <winrt/Windows.Devices.Bluetooth.Advertisement.h>
#include <winrt/Windows.Devices.Bluetooth.Rfcomm.h>
+#include <winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h>
+#include <winrt/Windows.Devices.Enumeration.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
+#include <winrt/Windows.Storage.Streams.h>
-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<IBluetoothLEAdvertisement> ad)
+static QByteArray byteArrayFromBuffer(const IBuffer &buffer)
+{
+ const uint8_t *data = buffer.data();
+ return QByteArray(reinterpret_cast<const char *>(data),
+ static_cast<qsizetype>(buffer.Length()));
+}
+
+static ManufacturerData extractManufacturerData(const BluetoothLEAdvertisement &ad)
{
ManufacturerData ret;
- ComPtr<IVector<BluetoothLEManufacturerData*>> 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<IBluetoothLEManufacturerData> 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<IBuffer> 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<IBluetoothLEAdvertisement
return ret;
}
-static ServiceData extractServiceData(ComPtr<IBluetoothLEAdvertisement> 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<IVectorView<BluetoothLEAdvertisementDataSection *>> 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<IBluetoothLEAdvertisementDataSection> 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<IBuffer> 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 &section : 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<quint16>(bufferData.constData())),
bufferData + 2);
- } else if (datatype == 0x20) {
+ } else if (dataType == 0x20) {
+ Q_ASSERT(bufferData.size() >= 4);
ret.insert(QBluetoothUuid(qFromLittleEndian<quint32>(bufferData.constData())),
bufferData + 4);
- } else if (datatype == 0x21) {
+ } else if (dataType == 0x21) {
+ Q_ASSERT(bufferData.size() >= 16);
ret.insert(QBluetoothUuid(qToBigEndian<quint128>(
qFromLittleEndian<quint128>(bufferData.constData()))),
bufferData + 16);
@@ -188,13 +132,99 @@ static ServiceData extractServiceData(ComPtr<IBluetoothLEAdvertisement> 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<AdvertisementWatcherWrapper>
+{
+ 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<QBluetoothUuid> &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<QBluetoothUuid> 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<QWinRTBluetoothDeviceDiscoveryWorker>
@@ -208,36 +238,27 @@ public:
private:
void startDeviceDiscovery(QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode);
- void onDeviceDiscoveryFinished(IAsyncOperation<DeviceInformationCollection *> *op,
- QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode);
- void gatherDeviceInformation(IDeviceInformation *deviceInfo,
- QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode);
- void gatherMultipleDeviceInformation(quint32 deviceCount, IVectorView<DeviceInformation *> *devices,
- QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode);
- void setupLEDeviceWatcher();
- void leBluetoothInfoFromDeviceIdAsync(HSTRING deviceId);
- void leBluetoothInfoFromAddressAsync(quint64 address);
- HRESULT onPairedBluetoothLEDeviceFoundAsync(IAsyncOperation<BluetoothLEDevice *> *op, AsyncStatus status);
- HRESULT onBluetoothLEDeviceFoundAsync(IAsyncOperation<BluetoothLEDevice *> *op, AsyncStatus status);
- enum PairingCheck {
- CheckForPairing,
- OmitPairingCheck
- };
- HRESULT onBluetoothLEDeviceFound(ComPtr<IBluetoothLEDevice> device, PairingCheck pairingCheck);
- HRESULT onBluetoothLEDeviceFound(ComPtr<IBluetoothLEDevice> device);
- HRESULT onBluetoothLEAdvertisementReceived(IBluetoothLEAdvertisementReceivedEventArgs *args);
- HRESULT onLeServicesReceived(IAsyncOperation<GenericAttributeProfile::GattDeviceServicesResult *> *op,
- AsyncStatus status, QBluetoothDeviceInfo &info);
std::shared_ptr<QBluetoothDeviceWatcherWinRT> 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<AdvertisementWatcherWrapper> 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<QBluetoothUuid> &uuids);
+
private:
- ComPtr<IBluetoothLEAdvertisementWatcher> m_leWatcher;
- EventRegistrationToken m_leDeviceAddedToken;
- QMutex m_foundDevicesMutex;
struct LEAdvertisingInfo {
QList<QBluetoothUuid> services;
ManufacturerData manufacturerData;
@@ -270,16 +290,17 @@ private:
qint16 rssi = 0;
};
+ quint8 requestedModes = 0;
+ QMutex m_leDevicesMutex;
QMap<quint64, LEAdvertisingInfo> m_foundLEDevicesMap;
- int m_pendingPairedDevices = 0;
-
- ComPtr<IBluetoothLEDeviceStatics> m_leDeviceStatics;
+ int m_pendingDevices = 0;
static constexpr int ClassicWatcherId = 1;
static constexpr int LowEnergyWatcherId = 2;
std::shared_ptr<QBluetoothDeviceWatcherWinRT> m_classicWatcher;
std::shared_ptr<QBluetoothDeviceWatcherWinRT> m_lowEnergyWatcher;
+ std::shared_ptr<AdvertisementWatcherWrapper> m_advertisementWatcher;
bool m_classicScanStarted = false;
bool m_lowEnergyScanStarted = false;
};
@@ -302,11 +323,11 @@ QWinRTBluetoothDeviceDiscoveryWorker::QWinRTBluetoothDeviceDiscoveryWorker()
qRegisterMetaType<ManufacturerData>();
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<IDeviceInformationStatics> 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<IAsyncOperation<DeviceInformationCollection *>> 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<QWinRTBluetoothDeviceDiscoveryWorker> thisPointer(this);
- hr = op->put_Completed(
- Callback<IAsyncOperationCompletedHandler<DeviceInformationCollection *>>([thisPointer, mode](IAsyncOperation<DeviceInformationCollection *> *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<DeviceInformationCollection *> *op, QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode)
+void QWinRTBluetoothDeviceDiscoveryWorker::finishDiscovery()
{
- qCDebug(QT_BT_WINDOWS) << (mode == QBluetoothDeviceDiscoveryAgent::ClassicMethod ? "BT" : "BTLE")
- << "scan completed";
- ComPtr<IVectorView<DeviceInformation *>> 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<DeviceInformation *> *devices, QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode)
+void QWinRTBluetoothDeviceDiscoveryWorker::onDeviceEnumerationCompleted(int watcherId)
{
- for (quint32 i = 0; i < deviceCount; ++i) {
- ComPtr<IDeviceInformation> 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<QBluetoothUuid> &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<IBluetoothLEAdvertisement> 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<IVector<GUID>> 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<QBluetoothUuid> 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<QBluetoothUuid> 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<QWinRTBluetoothDeviceDiscoveryWorker> thisPointer(this);
- hr = m_leWatcher->add_Received(
- Callback<ITypedEventHandler<BluetoothLEAdvertisementWatcher *, BluetoothLEAdvertisementReceivedEventArgs *>>(
- [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<QBluetoothDeviceWatcherWinRT>
QWinRTBluetoothDeviceDiscoveryWorker::createDeviceWatcher(winrt::hstring selector, int watcherId)
{
auto watcher = std::make_shared<QBluetoothDeviceWatcherWinRT>(
- 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<int>(error);
return;
@@ -676,16 +583,7 @@ void QWinRTBluetoothDeviceDiscoveryWorker::handleRfcommServices(
QList<QBluetoothUuid> 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<IAsyncOperation<BluetoothLEDevice *>> 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<QWinRTBluetoothDeviceDiscoveryWorker> thisPointer(this);
- hr = deviceFromIdOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<BluetoothLEDevice *>>
- ([thisPointer] (IAsyncOperation<BluetoothLEDevice *> *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<IAsyncOperation<BluetoothLEDevice *>> 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<QWinRTBluetoothDeviceDiscoveryWorker> thisPointer(this);
- hr = deviceFromAddressOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<BluetoothLEDevice *>>
- ([thisPointer](IAsyncOperation<BluetoothLEDevice *> *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<BluetoothLEDevice *> *op, AsyncStatus status)
-{
- --m_pendingPairedDevices;
- if (status != AsyncStatus::Completed)
- return S_OK;
-
- ComPtr<IBluetoothLEDevice> 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<BluetoothLEDevice *> *op, AsyncStatus status)
-{
- if (status != AsyncStatus::Completed)
- return S_OK;
-
- ComPtr<IBluetoothLEDevice> 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<IBluetoothLEDevice> 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<IBluetoothLEDevice2> device2;
- hr = device.As(&device2);
- EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Could not cast device",
- QBluetoothDeviceDiscoveryAgent::Error::UnknownError,
- return S_OK);
- ComPtr<IDeviceInformation> 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<IDeviceInformation2> 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<IDeviceInformationPairing> 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<IB
// Use the services obtained from the advertisement data if the device is not paired
if (!isPaired) {
info.setServiceUuids(adInfo.services);
- invokeDeviceFoundWithDebug(this, info);
+ invokeDecrementPendingDevicesCountAndCheckFinished(this);
+ invokeDeviceFoundWithDebug(info);
} else {
- ComPtr<IBluetoothLEDevice3> device3;
- hr = device.As(&device3);
- EMIT_WORKER_ERROR_AND_RETURN_IF_FAILED("Failed to obtain IBluetoothLEDevice3 instance",
- QBluetoothDeviceDiscoveryAgent::Error::UnknownError,
- return S_OK);
-
- ComPtr<IAsyncOperation<GenericAttributeProfile::GattDeviceServicesResult *>> 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<QWinRTBluetoothDeviceDiscoveryWorker> thisPtr(this);
- hr = servicesOp->put_Completed(
- Callback<IAsyncOperationCompletedHandler<
- GenericAttributeProfile::GattDeviceServicesResult *>>([thisPtr, info](
- IAsyncOperation<GenericAttributeProfile::GattDeviceServicesResult *> *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<GenericAttributeProfile::GattDeviceServicesResult *> *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<int>(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<GenericAttributeProfile::IGattDeviceServicesResult> 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<GenericAttributeProfile::GattDeviceService *> *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<QBluetoothUuid> uuids;
- for (uint i = 0; i < serviceCount; ++i) {
- ComPtr<GenericAttributeProfile::IGattDeviceService> 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<int>(commStatus);
+ << static_cast<int>(status);
}
- invokeDeviceFoundWithDebug(this, info);
+ invokeDeviceFoundWithDebug(info);
+}
- return S_OK;
+std::shared_ptr<AdvertisementWatcherWrapper>
+QWinRTBluetoothDeviceDiscoveryWorker::createAdvertisementWatcher()
+{
+ auto watcher = std::make_shared<AdvertisementWatcherWrapper>();
+ if (watcher) {
+ connect(watcher.get(), &AdvertisementWatcherWrapper::advertisementDataReceived,
+ this, &QWinRTBluetoothDeviceDiscoveryWorker::onAdvertisementDataReceived,
+ Qt::QueuedConnection);
+ }
+ return watcher;
}
QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(