Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtBluetooth module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qbluetoothdevicediscoveryagent.h"
41#include "qbluetoothdevicediscoveryagent_p.h"
42#include "qbluetoothaddress.h"
43#include "qbluetoothuuid.h"
44
45#ifdef CLASSIC_APP_BUILD
46#define Q_OS_WINRT
47#endif
48#include "qfunctions_winrt.h"
49
50#include <QtBluetooth/private/qtbluetoothglobal_p.h>
51#include <QtBluetooth/private/qbluetoothutils_winrt_p.h>
52#include <QtCore/QLoggingCategory>
53#include <QtCore/private/qeventdispatcher_winrt_p.h>
54
55#include <robuffer.h>
56#include <wrl.h>
57#include <windows.devices.enumeration.h>
58#include <windows.devices.bluetooth.h>
59#include <windows.foundation.collections.h>
60#include <windows.storage.streams.h>
61
62#include <windows.devices.bluetooth.advertisement.h>
63
64using namespace Microsoft::WRL;
65using namespace Microsoft::WRL::Wrappers;
66using namespace ABI::Windows::Foundation;
67using namespace ABI::Windows::Foundation::Collections;
68using namespace ABI::Windows::Devices;
69using namespace ABI::Windows::Devices::Bluetooth;
70using namespace ABI::Windows::Devices::Bluetooth::Advertisement;
71using namespace ABI::Windows::Devices::Enumeration;
72using namespace ABI::Windows::Storage::Streams;
73
74QT_BEGIN_NAMESPACE
75
76Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
77
78#define WARN_AND_RETURN_IF_FAILED(msg, ret) \
79 if (FAILED(hr)) { \
80 qCWarning(QT_BT_WINRT) << msg; \
81 ret; \
82 }
83
84#define WARN_AND_CONTINUE_IF_FAILED(msg) \
85 if (FAILED(hr)) { \
86 qCWarning(QT_BT_WINRT) << msg; \
87 continue; \
88 }
89
90static ManufacturerData extractManufacturerData(ComPtr<IBluetoothLEAdvertisement> ad)
91{
92 ManufacturerData ret;
93 ComPtr<IVector<BluetoothLEManufacturerData*>> data;
94 HRESULT hr = ad->get_ManufacturerData(&data);
95 WARN_AND_RETURN_IF_FAILED("Could not obtain list of manufacturer data.", return ret);
96 quint32 size;
97 hr = data->get_Size(&size);
98 WARN_AND_RETURN_IF_FAILED("Could not obtain manufacturer data's list size.", return ret);
99 for (quint32 i = 0; i < size; ++i) {
100 ComPtr<IBluetoothLEManufacturerData> d;
101 hr = data->GetAt(i, &d);
102 WARN_AND_CONTINUE_IF_FAILED("Could not obtain manufacturer data.");
103 quint16 id;
104 hr = d->get_CompanyId(&id);
105 WARN_AND_CONTINUE_IF_FAILED("Could not obtain manufacturer data company id.");
106 ComPtr<IBuffer> buffer;
107 hr = d->get_Data(&buffer);
108 WARN_AND_CONTINUE_IF_FAILED("Could not obtain manufacturer data set.");
109 const QByteArray bufferData = byteArrayFromBuffer(buffer);
110 if (ret.contains(id))
111 qCWarning(QT_BT_WINRT) << "Company ID already present in manufacturer data.";
112 ret.insert(id, bufferData);
113 }
114 return ret;
115}
116
117class QWinRTBluetoothDeviceDiscoveryWorker : public QObject
118{
119 Q_OBJECT
120public:
121 explicit QWinRTBluetoothDeviceDiscoveryWorker(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods);
122 ~QWinRTBluetoothDeviceDiscoveryWorker();
123 void start();
124 void stopLEWatcher();
125
126private:
127 void startDeviceDiscovery(QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode);
128 void onDeviceDiscoveryFinished(IAsyncOperation<DeviceInformationCollection *> *op,
129 QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode);
130 void gatherDeviceInformation(IDeviceInformation *deviceInfo,
131 QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode);
132 void gatherMultipleDeviceInformation(quint32 deviceCount, IVectorView<DeviceInformation *> *devices,
133 QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode);
134 void setupLEDeviceWatcher();
135 void classicBluetoothInfoFromDeviceIdAsync(HSTRING deviceId);
136 void leBluetoothInfoFromDeviceIdAsync(HSTRING deviceId);
137 void leBluetoothInfoFromAddressAsync(quint64 address);
138 HRESULT onPairedClassicBluetoothDeviceFoundAsync(IAsyncOperation<BluetoothDevice *> *op, AsyncStatus status );
139 HRESULT onPairedBluetoothLEDeviceFoundAsync(IAsyncOperation<BluetoothLEDevice *> *op, AsyncStatus status);
140 HRESULT onBluetoothLEDeviceFoundAsync(IAsyncOperation<BluetoothLEDevice *> *op, AsyncStatus status);
141 enum PairingCheck {
142 CheckForPairing,
143 OmitPairingCheck
144 };
145 HRESULT onBluetoothLEDeviceFound(ComPtr<IBluetoothLEDevice> device, PairingCheck pairingCheck);
146#if QT_CONFIG(winrt_btle_no_pairing)
147 HRESULT onBluetoothLEDeviceFound(ComPtr<IBluetoothLEDevice> device);
148#endif
149
150public slots:
151 void finishDiscovery();
152
153Q_SIGNALS:
154 void deviceFound(const QBluetoothDeviceInfo &info);
155 void deviceDataChanged(const QBluetoothAddress &address, QBluetoothDeviceInfo::Fields,
156 qint16 rssi, ManufacturerData manufacturerData);
157 void scanFinished();
158
159public:
160 quint8 requestedModes;
161
162private:
163 ComPtr<IBluetoothLEAdvertisementWatcher> m_leWatcher;
164 EventRegistrationToken m_leDeviceAddedToken;
165#if QT_CONFIG(winrt_btle_no_pairing)
166 QMutex m_foundDevicesMutex;
167 struct LEAdvertisingInfo {
168 QVector<QBluetoothUuid> services;
169 qint16 rssi = 0;
170 };
171
172 QMap<quint64, LEAdvertisingInfo> m_foundLEDevicesMap;
173#endif
174 QMap<quint64, qint16> m_foundLEDevices;
175 QMap<quint64, ManufacturerData> m_foundLEManufacturerData;
176 int m_pendingPairedDevices;
177
178 ComPtr<IBluetoothDeviceStatics> m_deviceStatics;
179 ComPtr<IBluetoothLEDeviceStatics> m_leDeviceStatics;
180};
181
182QWinRTBluetoothDeviceDiscoveryWorker::QWinRTBluetoothDeviceDiscoveryWorker(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods)
183 : requestedModes(methods)
184 , m_pendingPairedDevices(0)
185{
186 qRegisterMetaType<QBluetoothDeviceInfo>();
187 qRegisterMetaType<QBluetoothDeviceInfo::Fields>();
188 qRegisterMetaType<ManufacturerData>();
189
190#ifdef CLASSIC_APP_BUILD
191 CoInitialize(NULL);
192#endif
193 HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothDevice).Get(), &m_deviceStatics);
194 Q_ASSERT_SUCCEEDED(hr);
195 hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice).Get(), &m_leDeviceStatics);
196 Q_ASSERT_SUCCEEDED(hr);
197}
198
199QWinRTBluetoothDeviceDiscoveryWorker::~QWinRTBluetoothDeviceDiscoveryWorker()
200{
201 stopLEWatcher();
202#ifdef CLASSIC_APP_BUILD
203 CoUninitialize();
204#endif
205}
206
207void QWinRTBluetoothDeviceDiscoveryWorker::start()
208{
209 QEventDispatcherWinRT::runOnXamlThread([this]() {
210 if (requestedModes & QBluetoothDeviceDiscoveryAgent::ClassicMethod)
211 startDeviceDiscovery(QBluetoothDeviceDiscoveryAgent::ClassicMethod);
212
213 if (requestedModes & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod) {
214 startDeviceDiscovery(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
215 setupLEDeviceWatcher();
216 }
217 return S_OK;
218 });
219
220 qCDebug(QT_BT_WINRT) << "Worker started";
221}
222
223void QWinRTBluetoothDeviceDiscoveryWorker::stopLEWatcher()
224{
225 if (m_leWatcher) {
226 HRESULT hr = m_leWatcher->Stop();
227 Q_ASSERT_SUCCEEDED(hr);
228 if (m_leDeviceAddedToken.value) {
229 hr = m_leWatcher->remove_Received(m_leDeviceAddedToken);
230 Q_ASSERT_SUCCEEDED(hr);
231 }
232 }
233}
234
235void QWinRTBluetoothDeviceDiscoveryWorker::startDeviceDiscovery(QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode)
236{
237 HString deviceSelector;
238 ComPtr<IDeviceInformationStatics> deviceInformationStatics;
239 HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation).Get(), &deviceInformationStatics);
240 WARN_AND_RETURN_IF_FAILED("Could not obtain device information statics", return);
241 if (mode == QBluetoothDeviceDiscoveryAgent::LowEnergyMethod)
242 m_leDeviceStatics->GetDeviceSelector(deviceSelector.GetAddressOf());
243 else
244 m_deviceStatics->GetDeviceSelector(deviceSelector.GetAddressOf());
245 ComPtr<IAsyncOperation<DeviceInformationCollection *>> op;
246 hr = deviceInformationStatics->FindAllAsyncAqsFilter(deviceSelector.Get(), &op);
247 WARN_AND_RETURN_IF_FAILED("Could not start bluetooth device discovery operation", return);
248 QPointer<QWinRTBluetoothDeviceDiscoveryWorker> thisPointer(this);
249 hr = op->put_Completed(
250 Callback<IAsyncOperationCompletedHandler<DeviceInformationCollection *>>([thisPointer, mode](IAsyncOperation<DeviceInformationCollection *> *op, AsyncStatus status) {
251 if (status == Completed && thisPointer)
252 thisPointer->onDeviceDiscoveryFinished(op, mode);
253 return S_OK;
254 }).Get());
255 WARN_AND_RETURN_IF_FAILED("Could not add callback to bluetooth device discovery operation", return);
256}
257
258void QWinRTBluetoothDeviceDiscoveryWorker::onDeviceDiscoveryFinished(IAsyncOperation<DeviceInformationCollection *> *op, QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode)
259{
260 qCDebug(QT_BT_WINRT) << (mode == QBluetoothDeviceDiscoveryAgent::ClassicMethod ? "BT" : "BTLE")
261 << " scan completed";
262 ComPtr<IVectorView<DeviceInformation *>> devices;
263 HRESULT hr;
264 hr = op->GetResults(&devices);
265 Q_ASSERT_SUCCEEDED(hr);
266 quint32 deviceCount;
267 hr = devices->get_Size(&deviceCount);
268 Q_ASSERT_SUCCEEDED(hr);
269
270 // For classic discovery only paired devices will be found. If we only do classic disovery and
271 // no device is found, the scan is finished.
272 if (requestedModes == QBluetoothDeviceDiscoveryAgent::ClassicMethod &&
273 deviceCount == 0) {
274 finishDiscovery();
275 return;
276 }
277
278 m_pendingPairedDevices += deviceCount;
279 gatherMultipleDeviceInformation(deviceCount, devices.Get(), mode);
280}
281
282void QWinRTBluetoothDeviceDiscoveryWorker::gatherDeviceInformation(IDeviceInformation *deviceInfo, QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode)
283{
284 HString deviceId;
285 HRESULT hr;
286 hr = deviceInfo->get_Id(deviceId.GetAddressOf());
287 Q_ASSERT_SUCCEEDED(hr);
288 if (mode == QBluetoothDeviceDiscoveryAgent::LowEnergyMethod)
289 leBluetoothInfoFromDeviceIdAsync(deviceId.Get());
290 else
291 classicBluetoothInfoFromDeviceIdAsync(deviceId.Get());
292}
293
294void QWinRTBluetoothDeviceDiscoveryWorker::gatherMultipleDeviceInformation(quint32 deviceCount, IVectorView<DeviceInformation *> *devices, QBluetoothDeviceDiscoveryAgent::DiscoveryMethod mode)
295{
296 for (quint32 i = 0; i < deviceCount; ++i) {
297 ComPtr<IDeviceInformation> device;
298 HRESULT hr;
299 hr = devices->GetAt(i, &device);
300 Q_ASSERT_SUCCEEDED(hr);
301 gatherDeviceInformation(device.Get(), mode);
302 }
303}
304
305void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher()
306{
307 HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisementWatcher).Get(), &m_leWatcher);
308 Q_ASSERT_SUCCEEDED(hr);
309#if QT_CONFIG(winrt_btle_no_pairing)
310 if (supportsNewLEApi()) {
311 hr = m_leWatcher->put_ScanningMode(BluetoothLEScanningMode_Active);
312 Q_ASSERT_SUCCEEDED(hr);
313 }
314#endif // winrt_btle_no_pairing
315 hr = m_leWatcher->add_Received(Callback<ITypedEventHandler<BluetoothLEAdvertisementWatcher *, BluetoothLEAdvertisementReceivedEventArgs *>>([this](IBluetoothLEAdvertisementWatcher *, IBluetoothLEAdvertisementReceivedEventArgs *args) {
316 quint64 address;
317 HRESULT hr;
318 hr = args->get_BluetoothAddress(&address);
319 Q_ASSERT_SUCCEEDED(hr);
320 qint16 rssi;
321 hr = args->get_RawSignalStrengthInDBm(&rssi);
322 Q_ASSERT_SUCCEEDED(hr);
323 ComPtr<IBluetoothLEAdvertisement> ad;
324 hr = args->get_Advertisement(&ad);
325 Q_ASSERT_SUCCEEDED(hr);
326 const ManufacturerData manufacturerData = extractManufacturerData(ad);
327 QBluetoothDeviceInfo::Fields changedFields = QBluetoothDeviceInfo::Field::None;
328 if (!m_foundLEManufacturerData.contains(address)) {
329 m_foundLEManufacturerData.insert(address, manufacturerData);
330 changedFields.setFlag(QBluetoothDeviceInfo::Field::ManufacturerData);
331 } else if (m_foundLEManufacturerData.value(address) != manufacturerData) {
332 m_foundLEManufacturerData[address] = manufacturerData;
333 changedFields.setFlag(QBluetoothDeviceInfo::Field::ManufacturerData);
334 }
335#if QT_CONFIG(winrt_btle_no_pairing)
336 if (supportsNewLEApi()) {
337 ComPtr<IVector<GUID>> guids;
338 hr = ad->get_ServiceUuids(&guids);
339 Q_ASSERT_SUCCEEDED(hr);
340 quint32 size;
341 hr = guids->get_Size(&size);
342 Q_ASSERT_SUCCEEDED(hr);
343 QVector<QBluetoothUuid> serviceUuids;
344 for (quint32 i = 0; i < size; ++i) {
345 GUID guid;
346 hr = guids->GetAt(i, &guid);
347 Q_ASSERT_SUCCEEDED(hr);
348 QBluetoothUuid uuid(guid);
349 serviceUuids.append(uuid);
350 }
351 QMutexLocker locker(&m_foundDevicesMutex);
352 // Merge newly found services with list of currently found ones
353 if (m_foundLEDevicesMap.contains(address)) {
354 if (size == 0)
355 return S_OK;
356 const LEAdvertisingInfo adInfo = m_foundLEDevicesMap.value(address);
357 QVector<QBluetoothUuid> foundServices = adInfo.services;
358 if (adInfo.rssi != rssi) {
359 m_foundLEDevicesMap[address].rssi = rssi;
360 changedFields.setFlag(QBluetoothDeviceInfo::Field::RSSI);
361 }
362 bool newServiceAdded = false;
363 for (const QBluetoothUuid &uuid : qAsConst(serviceUuids)) {
364 if (!foundServices.contains(uuid)) {
365 foundServices.append(uuid);
366 newServiceAdded = true;
367 }
368 }
369 if (!newServiceAdded) {
370 if (!changedFields.testFlag(QBluetoothDeviceInfo::Field::None)) {
371 QMetaObject::invokeMethod(this, "deviceDataChanged", Qt::AutoConnection,
372 Q_ARG(QBluetoothAddress, QBluetoothAddress(address)),
373 Q_ARG(QBluetoothDeviceInfo::Fields, changedFields),
374 Q_ARG(qint16, rssi),
375 Q_ARG(ManufacturerData, manufacturerData));
376 }
377 return S_OK;
378 }
379 m_foundLEDevicesMap[address].services = foundServices;
380 } else {
381 LEAdvertisingInfo info;
382 info.services = std::move(serviceUuids);
383 info.rssi = rssi;
384 m_foundLEDevicesMap.insert(address, info);
385 }
386
387 locker.unlock();
388 } else
389#endif
390 {
391 if (m_foundLEDevices.contains(address)) {
392 if (m_foundLEDevices.value(address) != rssi) {
393 m_foundLEDevices[address] = rssi;
394 changedFields.setFlag(QBluetoothDeviceInfo::Field::RSSI);
395 }
396 if (!changedFields.testFlag(QBluetoothDeviceInfo::Field::None)) {
397 QMetaObject::invokeMethod(this, "deviceDataChanged", Qt::AutoConnection,
398 Q_ARG(QBluetoothAddress, QBluetoothAddress(address)),
399 Q_ARG(QBluetoothDeviceInfo::Fields, changedFields),
400 Q_ARG(qint16, rssi),
401 Q_ARG(ManufacturerData, manufacturerData));
402 }
403 return S_OK;
404 }
405 m_foundLEDevices.insert(address, rssi);
406 }
407 leBluetoothInfoFromAddressAsync(address);
408 return S_OK;
409 }).Get(), &m_leDeviceAddedToken);
410 Q_ASSERT_SUCCEEDED(hr);
411 hr = m_leWatcher->Start();
412 Q_ASSERT_SUCCEEDED(hr);
413}
414
415void QWinRTBluetoothDeviceDiscoveryWorker::finishDiscovery()
416{
417 emit scanFinished();
418 stopLEWatcher();
419 deleteLater();
420}
421
422// "deviceFound" will be emitted at the end of the deviceFromIdOperation callback
423void QWinRTBluetoothDeviceDiscoveryWorker::classicBluetoothInfoFromDeviceIdAsync(HSTRING deviceId)
424{
425 HRESULT hr;
426 hr = QEventDispatcherWinRT::runOnXamlThread([deviceId, this]() {
427 ComPtr<IAsyncOperation<BluetoothDevice *>> deviceFromIdOperation;
428 // on Windows 10 FromIdAsync might ask for device permission. We cannot wait here but have to handle that asynchronously
429 HRESULT hr = m_deviceStatics->FromIdAsync(deviceId, &deviceFromIdOperation);
430 if (FAILED(hr)) {
431 --m_pendingPairedDevices;
432 if (!m_pendingPairedDevices
433 && !(requestedModes & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod))
434 finishDiscovery();
435 qCWarning(QT_BT_WINRT) << "Could not obtain bluetooth device from id";
436 return S_OK;
437 }
438 QPointer<QWinRTBluetoothDeviceDiscoveryWorker> thisPointer(this);
439 hr = deviceFromIdOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<BluetoothDevice *>>
440 ([thisPointer](IAsyncOperation<BluetoothDevice *> *op, AsyncStatus status)
441 {
442 if (status == Completed && thisPointer)
443 thisPointer->onPairedClassicBluetoothDeviceFoundAsync(op, status);
444 return S_OK;
445 }).Get());
446 if (FAILED(hr)) {
447 --m_pendingPairedDevices;
448 if (!m_pendingPairedDevices
449 && !(requestedModes & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod))
450 finishDiscovery();
451 qCWarning(QT_BT_WINRT) << "Could not register device found callback";
452 return S_OK;
453 }
454 return S_OK;
455 });
456 Q_ASSERT_SUCCEEDED(hr);
457}
458
459// "deviceFound" will be emitted at the end of the deviceFromIdOperation callback
460void QWinRTBluetoothDeviceDiscoveryWorker::leBluetoothInfoFromDeviceIdAsync(HSTRING deviceId)
461{
462 HRESULT hr;
463 hr = QEventDispatcherWinRT::runOnXamlThread([deviceId, this]() {
464 ComPtr<IAsyncOperation<BluetoothLEDevice *>> deviceFromIdOperation;
465 // on Windows 10 FromIdAsync might ask for device permission. We cannot wait here but have to handle that asynchronously
466 HRESULT hr = m_leDeviceStatics->FromIdAsync(deviceId, &deviceFromIdOperation);
467 if (FAILED(hr)) {
468 --m_pendingPairedDevices;
469 qCWarning(QT_BT_WINRT) << "Could not obtain bluetooth device from id";
470 return S_OK;
471 }
472 QPointer<QWinRTBluetoothDeviceDiscoveryWorker> thisPointer(this);
473 hr = deviceFromIdOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<BluetoothLEDevice *>>
474 ([thisPointer] (IAsyncOperation<BluetoothLEDevice *> *op, AsyncStatus status)
475 {
476 if (status == Completed && thisPointer)
477 thisPointer->onPairedBluetoothLEDeviceFoundAsync(op, status);
478 return S_OK;
479 }).Get());
480 if (FAILED(hr)) {
481 --m_pendingPairedDevices;
482 qCWarning(QT_BT_WINRT) << "Could not register device found callback";
483 return S_OK;
484 }
485 return S_OK;
486 });
487 Q_ASSERT_SUCCEEDED(hr);
488}
489
490// "deviceFound" will be emitted at the end of the deviceFromAdressOperation callback
491void QWinRTBluetoothDeviceDiscoveryWorker::leBluetoothInfoFromAddressAsync(quint64 address)
492{
493 HRESULT hr;
494 hr = QEventDispatcherWinRT::runOnXamlThread([address, this]() {
495 ComPtr<IAsyncOperation<BluetoothLEDevice *>> deviceFromAddressOperation;
496 // on Windows 10 FromBluetoothAddressAsync might ask for device permission. We cannot wait
497 // here but have to handle that asynchronously
498 HRESULT hr = m_leDeviceStatics->FromBluetoothAddressAsync(address, &deviceFromAddressOperation);
499 if (FAILED(hr)) {
500 qCWarning(QT_BT_WINRT) << "Could not obtain bluetooth device from address";
501 return S_OK;
502 }
503 QPointer<QWinRTBluetoothDeviceDiscoveryWorker> thisPointer(this);
504 hr = deviceFromAddressOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<BluetoothLEDevice *>>
505 ([thisPointer](IAsyncOperation<BluetoothLEDevice *> *op, AsyncStatus status)
506 {
507 if (status == Completed && thisPointer)
508 thisPointer->onBluetoothLEDeviceFoundAsync(op, status);
509 return S_OK;
510 }).Get());
511 if (FAILED(hr)) {
512 qCWarning(QT_BT_WINRT) << "Could not register device found callback";
513 return S_OK;
514 }
515 return S_OK;
516 });
517 Q_ASSERT_SUCCEEDED(hr);
518}
519
520HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onPairedClassicBluetoothDeviceFoundAsync(IAsyncOperation<BluetoothDevice *> *op, AsyncStatus status)
521{
522 --m_pendingPairedDevices;
523 if (status != AsyncStatus::Completed)
524 return S_OK;
525
526 ComPtr<IBluetoothDevice> device;
527 HRESULT hr = op->GetResults(&device);
528 Q_ASSERT_SUCCEEDED(hr);
529
530 if (!device)
531 return S_OK;
532
533 UINT64 address;
534 HString name;
535 ComPtr<IBluetoothClassOfDevice> classOfDevice;
536 UINT32 classOfDeviceInt;
537 hr = device->get_BluetoothAddress(&address);
538 Q_ASSERT_SUCCEEDED(hr);
539 hr = device->get_Name(name.GetAddressOf());
540 Q_ASSERT_SUCCEEDED(hr);
541 const QString btName = QString::fromWCharArray(WindowsGetStringRawBuffer(name.Get(), nullptr));
542 hr = device->get_ClassOfDevice(&classOfDevice);
543 Q_ASSERT_SUCCEEDED(hr);
544 hr = classOfDevice->get_RawValue(&classOfDeviceInt);
545 Q_ASSERT_SUCCEEDED(hr);
546 IVectorView <Rfcomm::RfcommDeviceService *> *deviceServices;
547 hr = device->get_RfcommServices(&deviceServices);
548 if (hr == E_ACCESSDENIED) {
549 qCWarning(QT_BT_WINRT) << "Could not obtain device services. Please check you have "
550 "permission to access the device.";
551 } else {
552 Q_ASSERT_SUCCEEDED(hr);
553 uint serviceCount;
554 hr = deviceServices->get_Size(&serviceCount);
555 Q_ASSERT_SUCCEEDED(hr);
556 QVector<QBluetoothUuid> uuids;
557 for (uint i = 0; i < serviceCount; ++i) {
558 ComPtr<Rfcomm::IRfcommDeviceService> service;
559 hr = deviceServices->GetAt(i, &service);
560 Q_ASSERT_SUCCEEDED(hr);
561 ComPtr<Rfcomm::IRfcommServiceId> id;
562 hr = service->get_ServiceId(&id);
563 Q_ASSERT_SUCCEEDED(hr);
564 GUID uuid;
565 hr = id->get_Uuid(&uuid);
566 Q_ASSERT_SUCCEEDED(hr);
567 uuids.append(QBluetoothUuid(uuid));
568 }
569
570 qCDebug(QT_BT_WINRT) << "Discovered BT device: " << QString::number(address) << btName
571 << "Num UUIDs" << uuids.count();
572
573 QBluetoothDeviceInfo info(QBluetoothAddress(address), btName, classOfDeviceInt);
574 info.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration);
575 info.setServiceUuids(uuids);
576 info.setCached(true);
577
578 QMetaObject::invokeMethod(this, "deviceFound", Qt::AutoConnection,
579 Q_ARG(QBluetoothDeviceInfo, info));
580 }
581 if (!m_pendingPairedDevices && !(requestedModes & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod))
582 finishDiscovery();
583 return S_OK;
584}
585
586HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onPairedBluetoothLEDeviceFoundAsync(IAsyncOperation<BluetoothLEDevice *> *op, AsyncStatus status)
587{
588 --m_pendingPairedDevices;
589 if (status != AsyncStatus::Completed)
590 return S_OK;
591
592 ComPtr<IBluetoothLEDevice> device;
593 HRESULT hr;
594 hr = op->GetResults(&device);
595 Q_ASSERT_SUCCEEDED(hr);
596#if QT_CONFIG(winrt_btle_no_pairing)
597 if (supportsNewLEApi())
598 return onBluetoothLEDeviceFound(device);
599 else
600#endif
601 return onBluetoothLEDeviceFound(device, OmitPairingCheck);
602}
603
604HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFoundAsync(IAsyncOperation<BluetoothLEDevice *> *op, AsyncStatus status)
605{
606 if (status != AsyncStatus::Completed)
607 return S_OK;
608
609 ComPtr<IBluetoothLEDevice> device;
610 HRESULT hr;
611 hr = op->GetResults(&device);
612 Q_ASSERT_SUCCEEDED(hr);
613#if QT_CONFIG(winrt_btle_no_pairing)
614 if (supportsNewLEApi())
615 return onBluetoothLEDeviceFound(device);
616 else
617#endif
618 return onBluetoothLEDeviceFound(device, PairingCheck::CheckForPairing);
619}
620
621HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr<IBluetoothLEDevice> device, PairingCheck pairingCheck)
622{
623 if (!device) {
624 qCDebug(QT_BT_WINRT) << "onBluetoothLEDeviceFound: No device given";
625 return S_OK;
626 }
627
628 if (pairingCheck == CheckForPairing) {
629 ComPtr<IBluetoothLEDevice2> device2;
630 HRESULT hr = device.As(&device2);
631 Q_ASSERT_SUCCEEDED(hr);
632 ComPtr<IDeviceInformation> deviceInfo;
633 hr = device2->get_DeviceInformation(&deviceInfo);
634 Q_ASSERT_SUCCEEDED(hr);
635 if (!deviceInfo) {
636 qCDebug(QT_BT_WINRT) << "onBluetoothLEDeviceFound: Could not obtain device information";
637 return S_OK;
638 }
639 ComPtr<IDeviceInformation2> deviceInfo2;
640 hr = deviceInfo.As(&deviceInfo2);
641 Q_ASSERT_SUCCEEDED(hr);
642 ComPtr<IDeviceInformationPairing> pairing;
643 hr = deviceInfo2->get_Pairing(&pairing);
644 Q_ASSERT_SUCCEEDED(hr);
645 boolean isPaired;
646 hr = pairing->get_IsPaired(&isPaired);
647 Q_ASSERT_SUCCEEDED(hr);
648 // We need a paired device in order to be able to obtain its information
649 if (!isPaired) {
650 ComPtr<IAsyncOperation<DevicePairingResult *>> pairingOp;
651 QPointer<QWinRTBluetoothDeviceDiscoveryWorker> tPointer(this);
652 hr = pairing.Get()->PairAsync(&pairingOp);
653 Q_ASSERT_SUCCEEDED(hr);
654 pairingOp->put_Completed(
655 Callback<IAsyncOperationCompletedHandler<DevicePairingResult *>>([device, tPointer](IAsyncOperation<DevicePairingResult *> *op, AsyncStatus status) {
656 if (!tPointer)
657 return S_OK;
658
659 if (status != AsyncStatus::Completed) {
660 qCDebug(QT_BT_WINRT) << "Could not pair device";
661 return S_OK;
662 }
663
664 ComPtr<IDevicePairingResult> result;
665 op->GetResults(&result);
666
667 DevicePairingResultStatus pairingStatus;
668 result.Get()->get_Status(&pairingStatus);
669
670 if (pairingStatus != DevicePairingResultStatus_Paired) {
671 qCDebug(QT_BT_WINRT) << "Could not pair device";
672 return S_OK;
673 }
674
675 tPointer->onBluetoothLEDeviceFound(device, OmitPairingCheck);
676 return S_OK;
677 }).Get());
678 return S_OK;
679 }
680 }
681
682 UINT64 address;
683 HString name;
684 HRESULT hr = device->get_BluetoothAddress(&address);
685 Q_ASSERT_SUCCEEDED(hr);
686 hr = device->get_Name(name.GetAddressOf());
687 Q_ASSERT_SUCCEEDED(hr);
688 const QString btName = QString::fromWCharArray(WindowsGetStringRawBuffer(name.Get(), nullptr));
689 IVectorView <GenericAttributeProfile::GattDeviceService *> *deviceServices;
690 hr = device->get_GattServices(&deviceServices);
691 Q_ASSERT_SUCCEEDED(hr);
692 uint serviceCount;
693 hr = deviceServices->get_Size(&serviceCount);
694 Q_ASSERT_SUCCEEDED(hr);
695 QVector<QBluetoothUuid> uuids;
696 for (uint i = 0; i < serviceCount; ++i) {
697 ComPtr<GenericAttributeProfile::IGattDeviceService> service;
698 hr = deviceServices->GetAt(i, &service);
699 Q_ASSERT_SUCCEEDED(hr);
700 ComPtr<Rfcomm::IRfcommServiceId> id;
701 GUID uuid;
702 hr = service->get_Uuid(&uuid);
703 Q_ASSERT_SUCCEEDED(hr);
704 uuids.append(QBluetoothUuid(uuid));
705 }
706 const qint16 rssi = m_foundLEDevices.value(address);
707 const ManufacturerData manufacturerData = m_foundLEManufacturerData.value(address);
708
709 qCDebug(QT_BT_WINRT) << "Discovered BTLE device: " << QString::number(address) << btName
710 << "Num UUIDs" << uuids.count() << "RSSI:" << rssi
711 << "Num manufacturer data" << manufacturerData.count();
712
713 QBluetoothDeviceInfo info(QBluetoothAddress(address), btName, 0);
714 info.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
715 info.setServiceUuids(uuids);
716 info.setRssi(rssi);
717 for (const quint16 key : manufacturerData.keys())
718 info.setManufacturerData(key, manufacturerData.value(key));
719 info.setCached(true);
720
721 QMetaObject::invokeMethod(this, "deviceFound", Qt::AutoConnection,
722 Q_ARG(QBluetoothDeviceInfo, info));
723 return S_OK;
724}
725
726#if QT_CONFIG(winrt_btle_no_pairing)
727HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr<IBluetoothLEDevice> device)
728{
729 if (!device) {
730 qCDebug(QT_BT_WINRT) << "onBluetoothLEDeviceFound: No device given";
731 return S_OK;
732 }
733
734 UINT64 address;
735 HString name;
736 HRESULT hr = device->get_BluetoothAddress(&address);
737 Q_ASSERT_SUCCEEDED(hr);
738 hr = device->get_Name(name.GetAddressOf());
739 Q_ASSERT_SUCCEEDED(hr);
740 const QString btName = QString::fromWCharArray(WindowsGetStringRawBuffer(name.Get(), nullptr));
741
742 ComPtr<IBluetoothLEDevice2> device2;
743 hr = device.As(&device2);
744 Q_ASSERT_SUCCEEDED(hr);
745 ComPtr<IDeviceInformation> deviceInfo;
746 hr = device2->get_DeviceInformation(&deviceInfo);
747 Q_ASSERT_SUCCEEDED(hr);
748 if (!deviceInfo) {
749 qCDebug(QT_BT_WINRT) << "onBluetoothLEDeviceFound: Could not obtain device information";
750 return S_OK;
751 }
752 ComPtr<IDeviceInformation2> deviceInfo2;
753 hr = deviceInfo.As(&deviceInfo2);
754 Q_ASSERT_SUCCEEDED(hr);
755 ComPtr<IDeviceInformationPairing> pairing;
756 hr = deviceInfo2->get_Pairing(&pairing);
757 Q_ASSERT_SUCCEEDED(hr);
758 boolean isPaired;
759 hr = pairing->get_IsPaired(&isPaired);
760 Q_ASSERT_SUCCEEDED(hr);
761 QVector<QBluetoothUuid> uuids;
762
763 const LEAdvertisingInfo adInfo = m_foundLEDevicesMap.value(address);
764 const qint16 rssi = adInfo.rssi;
765 // Use the services obtained from the advertisement data if the device is not paired
766 if (!isPaired) {
767 uuids = adInfo.services;
768 } else {
769 IVectorView <GenericAttributeProfile::GattDeviceService *> *deviceServices;
770 hr = device->get_GattServices(&deviceServices);
771 Q_ASSERT_SUCCEEDED(hr);
772 uint serviceCount;
773 hr = deviceServices->get_Size(&serviceCount);
774 Q_ASSERT_SUCCEEDED(hr);
775 for (uint i = 0; i < serviceCount; ++i) {
776 ComPtr<GenericAttributeProfile::IGattDeviceService> service;
777 hr = deviceServices->GetAt(i, &service);
778 Q_ASSERT_SUCCEEDED(hr);
779 GUID uuid;
780 hr = service->get_Uuid(&uuid);
781 Q_ASSERT_SUCCEEDED(hr);
782 uuids.append(QBluetoothUuid(uuid));
783 }
784 }
785 const ManufacturerData manufacturerData = m_foundLEManufacturerData.value(address);
786
787 qCDebug(QT_BT_WINRT) << "Discovered BTLE device: " << QString::number(address) << btName
788 << "Num UUIDs" << uuids.count() << "RSSI:" << rssi
789 << "Num manufacturer data" << manufacturerData.count();
790
791 QBluetoothDeviceInfo info(QBluetoothAddress(address), btName, 0);
792 info.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
793 info.setServiceUuids(uuids);
794 info.setRssi(rssi);
795 for (quint16 key : manufacturerData.keys())
796 info.setManufacturerData(key, manufacturerData.value(key));
797 info.setCached(true);
798
799 QMetaObject::invokeMethod(this, "deviceFound", Qt::AutoConnection,
800 Q_ARG(QBluetoothDeviceInfo, info));
801 return S_OK;
802}
803#endif // QT_CONFIG(winrt_btle_no_pairing)
804
805QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(
806 const QBluetoothAddress &deviceAdapter,
807 QBluetoothDeviceDiscoveryAgent *parent)
808
809 : inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry),
810 lastError(QBluetoothDeviceDiscoveryAgent::NoError),
811 lowEnergySearchTimeout(25000),
812 q_ptr(parent),
813 leScanTimer(0)
814{
815 Q_UNUSED(deviceAdapter);
816}
817
818QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate()
819{
820 disconnectAndClearWorker();
821}
822
823bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const
824{
825 return worker;
826}
827
828QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent::supportedDiscoveryMethods()
829{
830 return (ClassicMethod | LowEnergyMethod);
831}
832
833void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods)
834{
835 if (worker)
836 return;
837
838 worker = new QWinRTBluetoothDeviceDiscoveryWorker(methods);
839 discoveredDevices.clear();
840 connect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::deviceFound,
841 this, &QBluetoothDeviceDiscoveryAgentPrivate::registerDevice);
842 connect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::deviceDataChanged,
843 this, &QBluetoothDeviceDiscoveryAgentPrivate::updateDeviceData);
844 connect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::scanFinished,
845 this, &QBluetoothDeviceDiscoveryAgentPrivate::onScanFinished);
846 worker->start();
847
848 if (lowEnergySearchTimeout > 0 && methods & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod) { // otherwise no timeout and stop() required
849 if (!leScanTimer) {
850 leScanTimer = new QTimer(this);
851 leScanTimer->setSingleShot(true);
852 }
853 connect(leScanTimer, &QTimer::timeout,
854 worker, &QWinRTBluetoothDeviceDiscoveryWorker::finishDiscovery);
855 leScanTimer->setInterval(lowEnergySearchTimeout);
856 leScanTimer->start();
857 }
858}
859
860void QBluetoothDeviceDiscoveryAgentPrivate::stop()
861{
862 Q_Q(QBluetoothDeviceDiscoveryAgent);
863 if (worker) {
864 worker->stopLEWatcher();
865 disconnectAndClearWorker();
866 emit q->canceled();
867 }
868 if (leScanTimer) {
869 leScanTimer->stop();
870 worker->deleteLater();
871 }
872}
873
874void QBluetoothDeviceDiscoveryAgentPrivate::registerDevice(const QBluetoothDeviceInfo &info)
875{
876 Q_Q(QBluetoothDeviceDiscoveryAgent);
877
878 for (QList<QBluetoothDeviceInfo>::iterator iter = discoveredDevices.begin();
879 iter != discoveredDevices.end(); ++iter) {
880 if (iter->address() == info.address()) {
881 qCDebug(QT_BT_WINRT) << "Updating device" << iter->name() << iter->address();
882 // merge service uuids
883 QList<QBluetoothUuid> uuids = iter->serviceUuids();
884 uuids.append(info.serviceUuids());
885 const QSet<QBluetoothUuid> uuidSet = uuids.toSet();
886 if (iter->serviceUuids().count() != uuidSet.count())
887 iter->setServiceUuids(uuidSet.toList().toVector());
888 if (iter->coreConfigurations() != info.coreConfigurations())
889 iter->setCoreConfigurations(QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration);
890 return;
891 }
892 }
893
894 discoveredDevices << info;
895 emit q->deviceDiscovered(info);
896}
897
898void QBluetoothDeviceDiscoveryAgentPrivate::updateDeviceData(const QBluetoothAddress &address,
899 QBluetoothDeviceInfo::Fields fields,
900 qint16 rssi,
901 ManufacturerData manufacturerData)
902{
903 if (fields.testFlag(QBluetoothDeviceInfo::Field::None))
904 return;
905
906 Q_Q(QBluetoothDeviceDiscoveryAgent);
907 for (QList<QBluetoothDeviceInfo>::iterator iter = discoveredDevices.begin();
908 iter != discoveredDevices.end(); ++iter) {
909 if (iter->address() == address) {
910 qCDebug(QT_BT_WINRT) << "Updating data for device" << iter->name() << iter->address();
911 if (fields.testFlag(QBluetoothDeviceInfo::Field::RSSI))
912 iter->setRssi(rssi);
913 if (fields.testFlag(QBluetoothDeviceInfo::Field::ManufacturerData))
914 for (quint16 key : manufacturerData.keys())
915 iter->setManufacturerData(key, manufacturerData.value(key));
916 emit q->deviceUpdated(*iter, fields);
917 return;
918 }
919 }
920}
921
922void QBluetoothDeviceDiscoveryAgentPrivate::onScanFinished()
923{
924 Q_Q(QBluetoothDeviceDiscoveryAgent);
925 disconnectAndClearWorker();
926 emit q->finished();
927}
928
929void QBluetoothDeviceDiscoveryAgentPrivate::disconnectAndClearWorker()
930{
931 if (!worker)
932 return;
933
934 disconnect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::scanFinished,
935 this, &QBluetoothDeviceDiscoveryAgentPrivate::onScanFinished);
936 disconnect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::deviceFound,
937 this, &QBluetoothDeviceDiscoveryAgentPrivate::registerDevice);
938 disconnect(worker, &QWinRTBluetoothDeviceDiscoveryWorker::deviceDataChanged,
939 this, &QBluetoothDeviceDiscoveryAgentPrivate::updateDeviceData);
940 if (leScanTimer) {
941 disconnect(leScanTimer, &QTimer::timeout,
942 worker, &QWinRTBluetoothDeviceDiscoveryWorker::finishDiscovery);
943 }
944 worker.clear();
945}
946
947QT_END_NAMESPACE
948
949#include <qbluetoothdevicediscoveryagent_winrt.moc>
950

Warning: That file was not part of the compilation database. It may have many parsing errors.