Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qbluetoothservicediscoveryagent_winrt.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
8
9#include <QtCore/QLoggingCategory>
10#include <QtCore/private/qfunctions_winrt_p.h>
11
12#include <functional>
13#include <robuffer.h>
14#include <windows.devices.enumeration.h>
15#include <windows.devices.bluetooth.h>
16#include <windows.foundation.collections.h>
17#include <windows.networking.h>
18#include <windows.storage.streams.h>
19#include <wrl.h>
20
21using namespace Microsoft::WRL;
22using namespace Microsoft::WRL::Wrappers;
23using namespace ABI::Windows::Foundation;
24using namespace ABI::Windows::Foundation::Collections;
25using namespace ABI::Windows::Devices;
26using namespace ABI::Windows::Devices::Bluetooth;
27using namespace ABI::Windows::Devices::Bluetooth::Rfcomm;
28using namespace ABI::Windows::Devices::Enumeration;
29using namespace ABI::Windows::Storage::Streams;
30
34
36
38
39#define TYPE_UINT8 8
40#define TYPE_UINT16 9
41#define TYPE_UINT32 10
42#define TYPE_SHORT_UUID 25
43#define TYPE_LONG_UUID 28
44#define TYPE_STRING 37
45#define TYPE_SEQUENCE 53
46
47#define BREAK_IF_FAILED(msg) RETURN_IF_FAILED(msg, break)
48
49// Helper to reverse given uchar array
50static void reverseArray(uchar data[], size_t length)
51{
52 for (size_t i = length; i > length/2; i--)
53 std::swap(data[length - i], data[i - 1]);
54}
55
57{
59public:
63 void start();
64
67 void scanFinished(quint64 deviceAddress);
69
70private:
71 HRESULT onBluetoothDeviceFoundAsync(IAsyncOperation<BluetoothDevice *> *op, AsyncStatus status);
72
73 void processServiceSearchResult(quint64 address, ComPtr<IVectorView<RfcommDeviceService*>> services);
75
76private:
77 quint64 m_targetAddress;
79};
80
81QWinRTBluetoothServiceDiscoveryWorker::QWinRTBluetoothServiceDiscoveryWorker(quint64 targetAddress,
82 QBluetoothServiceDiscoveryAgent::DiscoveryMode mode)
83 : m_targetAddress(targetAddress)
84 , m_mode(mode)
85{
86}
87
91
93{
94 ComPtr<IBluetoothDeviceStatics> deviceStatics;
95 HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothDevice).Get(), &deviceStatics);
96 Q_ASSERT_SUCCEEDED(hr);
97 ComPtr<IAsyncOperation<BluetoothDevice *>> deviceFromAddressOperation;
98 hr = deviceStatics->FromBluetoothAddressAsync(m_targetAddress, &deviceFromAddressOperation);
99 Q_ASSERT_SUCCEEDED(hr);
100 hr = deviceFromAddressOperation->put_Completed(Callback<IAsyncOperationCompletedHandler<BluetoothDevice *>>
101 (this, &QWinRTBluetoothServiceDiscoveryWorker::onBluetoothDeviceFoundAsync).Get());
102 Q_ASSERT_SUCCEEDED(hr);
103}
104
105HRESULT QWinRTBluetoothServiceDiscoveryWorker::onBluetoothDeviceFoundAsync(IAsyncOperation<BluetoothDevice *> *op, AsyncStatus status)
106{
107 if (status != Completed) {
108 qCDebug(QT_BT_WINDOWS) << "Could not find device";
109 emit errorOccured();
110 return S_OK;
111 }
112
113 ComPtr<IBluetoothDevice> device;
114 HRESULT hr;
115 hr = op->GetResults(&device);
116 Q_ASSERT_SUCCEEDED(hr);
117 quint64 address;
118 device->get_BluetoothAddress(&address);
119
120 ComPtr<IBluetoothDevice3> device3;
121 hr = device.As(&device3);
122 Q_ASSERT_SUCCEEDED(hr);
123 ComPtr<IAsyncOperation<RfcommDeviceServicesResult *>> serviceOp;
124 const BluetoothCacheMode cacheMode = m_mode == QBluetoothServiceDiscoveryAgent::MinimalDiscovery
125 ? BluetoothCacheMode_Cached : BluetoothCacheMode_Uncached;
126 hr = device3->GetRfcommServicesWithCacheModeAsync(cacheMode, &serviceOp);
127 Q_ASSERT_SUCCEEDED(hr);
128 hr = serviceOp->put_Completed(Callback<IAsyncOperationCompletedHandler<RfcommDeviceServicesResult *>>
129 ([address, this](IAsyncOperation<RfcommDeviceServicesResult *> *op, AsyncStatus status)
130 {
131 if (status != Completed) {
132 qCDebug(QT_BT_WINDOWS) << "Could not obtain service list";
133 emit errorOccured();
134 return S_OK;
135 }
136
137 ComPtr<IRfcommDeviceServicesResult> result;
138 HRESULT hr = op->GetResults(&result);
139 Q_ASSERT_SUCCEEDED(hr);
140 ComPtr<IVectorView<RfcommDeviceService*>> commServices;
141 hr = result->get_Services(&commServices);
142 Q_ASSERT_SUCCEEDED(hr);
143 processServiceSearchResult(address, commServices);
144 return S_OK;
145 }).Get());
146 Q_ASSERT_SUCCEEDED(hr);
147
148 return S_OK;
149}
150
151void QWinRTBluetoothServiceDiscoveryWorker::processServiceSearchResult(quint64 address, ComPtr<IVectorView<RfcommDeviceService*>> services)
152{
153 quint32 size;
154 HRESULT hr;
155 hr = services->get_Size(&size);
156 Q_ASSERT_SUCCEEDED(hr);
157 for (quint32 i = 0; i < size; ++i) {
158 ComPtr<IRfcommDeviceService> service;
159 hr = services->GetAt(i, &service);
160 Q_ASSERT_SUCCEEDED(hr);
161 HString name;
162 hr = service->get_ConnectionServiceName(name.GetAddressOf());
163 Q_ASSERT_SUCCEEDED(hr);
164 const QString serviceName = QString::fromWCharArray(WindowsGetStringRawBuffer(name.Get(), nullptr));
165 ComPtr<ABI::Windows::Networking::IHostName> host;
166 hr = service->get_ConnectionHostName(host.GetAddressOf());
167 Q_ASSERT_SUCCEEDED(hr);
168 HString hostName;
169 hr = host->get_RawName(hostName.GetAddressOf());
170 Q_ASSERT_SUCCEEDED(hr);
171 const QString qHostName = QString::fromWCharArray(WindowsGetStringRawBuffer(hostName.Get(),
172 nullptr));
173 ComPtr<IRfcommServiceId> id;
174 hr = service->get_ServiceId(&id);
175 Q_ASSERT_SUCCEEDED(hr);
176 GUID guid;
177 hr = id->get_Uuid(&guid);
178 const QBluetoothUuid uuid(guid);
179 Q_ASSERT_SUCCEEDED(hr);
180
182 info.setAttribute(0xBEEF, QVariant(qHostName));
183 info.setAttribute(0xBEF0, QVariant(serviceName));
184 info.setServiceName(serviceName);
185 info.setServiceUuid(uuid);
186 ComPtr<IAsyncOperation<IMapView<UINT32, IBuffer *> *>> op;
187 hr = service->GetSdpRawAttributesAsync(op.GetAddressOf());
188 if (FAILED(hr)) {
189 emit errorOccured();
190 qCDebug(QT_BT_WINDOWS) << "Check manifest capabilities";
191 continue;
192 }
193 ComPtr<IMapView<UINT32, IBuffer *>> mapView;
194 hr = QWinRTFunctions::await(op, mapView.GetAddressOf());
195 Q_ASSERT_SUCCEEDED(hr);
196 // TODO timeout
197 ComPtr<ValueIterable> iterable;
198 ComPtr<ValueIterator> iterator;
199
200 hr = mapView.As(&iterable);
201 if (FAILED(hr))
202 continue;
203
204 boolean current = false;
205 hr = iterable->First(&iterator);
206 if (FAILED(hr))
207 continue;
208 hr = iterator->get_HasCurrent(&current);
209 if (FAILED(hr))
210 continue;
211
212 while (SUCCEEDED(hr) && current) {
213 ComPtr<ValueItem> item;
214 hr = iterator->get_Current(&item);
215 if (FAILED(hr))
216 continue;
217
218 UINT32 key;
219 hr = item->get_Key(&key);
220 if (FAILED(hr))
221 continue;
222
223 ComPtr<IBuffer> buffer;
224 hr = item->get_Value(&buffer);
225 Q_ASSERT_SUCCEEDED(hr);
226
227 ComPtr<IDataReader> dataReader;
228 ComPtr<IDataReaderStatics> dataReaderStatics;
229 hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataReader).Get(), &dataReaderStatics);
230 Q_ASSERT_SUCCEEDED(hr);
231 hr = dataReaderStatics->FromBuffer(buffer.Get(), dataReader.GetAddressOf());
232 Q_ASSERT_SUCCEEDED(hr);
233 BYTE type;
234 hr = dataReader->ReadByte(&type);
235 BREAK_IF_FAILED("type");
236 if (type == TYPE_UINT8) {
237 quint8 value;
238 hr = dataReader->ReadByte(&value);
239 BREAK_IF_FAILED("uint8 value");
240 info.setAttribute(key, value);
241 qCDebug(QT_BT_WINDOWS) << "UUID" << uuid << "KEY" << Qt::hex << key << "TYPE" << Qt::dec << type << "UINT8" << Qt::hex << value;
242 } else if (type == TYPE_UINT16) {
243 quint16 value;
244 hr = dataReader->ReadUInt16(&value);
245 BREAK_IF_FAILED("uint16 value");
246 info.setAttribute(key, value);
247 qCDebug(QT_BT_WINDOWS) << "UUID" << uuid << "KEY" << Qt::hex << key << "TYPE" << Qt::dec << type << "UINT16" << Qt::hex << value;
248 } else if (type == TYPE_UINT32) {
249 quint32 value;
250 hr = dataReader->ReadUInt32(&value);
251 BREAK_IF_FAILED("uint32 value");
252 info.setAttribute(key, value);
253 qCDebug(QT_BT_WINDOWS) << "UUID" << uuid << "KEY" << Qt::hex << key << "TYPE" << Qt::dec << type << "UINT32" << Qt::hex << value;
254 } else if (type == TYPE_SHORT_UUID) {
255 quint16 value;
256 hr = dataReader->ReadUInt16(&value);
257 BREAK_IF_FAILED("short uuid");
258 const QBluetoothUuid uuid(value);
259 info.setAttribute(key, uuid);
260 qCDebug(QT_BT_WINDOWS) << "UUID" << uuid << "KEY" << Qt::hex << key << "TYPE" << Qt::dec << type << "UUID" << Qt::hex << uuid;
261 } else if (type == TYPE_LONG_UUID) {
262 GUID value;
263 hr = dataReader->ReadGuid(&value);
264 BREAK_IF_FAILED("long uuid");
265 // The latter 8 bytes are in reverse order
266 reverseArray(value.Data4, sizeof(value.Data4)/sizeof(value.Data4[0]));
267 const QBluetoothUuid uuid(value);
268 info.setAttribute(key, uuid);
269 qCDebug(QT_BT_WINDOWS) << "UUID" << uuid << "KEY" << Qt::hex << key << "TYPE" << Qt::dec << type << "UUID" << Qt::hex << uuid;
270 } else if (type == TYPE_STRING) {
271 BYTE length;
272 hr = dataReader->ReadByte(&length);
273 BREAK_IF_FAILED("string length");
274 HString value;
275 hr = dataReader->ReadString(length, value.GetAddressOf());
276 BREAK_IF_FAILED("string value");
277 const QString str = QString::fromWCharArray(WindowsGetStringRawBuffer(value.Get(), nullptr));
278 info.setAttribute(key, str);
279 qCDebug(QT_BT_WINDOWS) << "UUID" << uuid << "KEY" << Qt::hex << key << "TYPE" << Qt::dec << type << "STRING" << str;
280 } else if (type == TYPE_SEQUENCE) {
281 bool ok;
282 QBluetoothServiceInfo::Sequence sequence = readSequence(dataReader, &ok, nullptr);
283 if (ok) {
284 info.setAttribute(key, sequence);
285 qCDebug(QT_BT_WINDOWS) << "UUID" << uuid << "KEY" << Qt::hex << key << "TYPE" << Qt::dec << type << "SEQUENCE" << sequence;
286 } else {
287 qCDebug(QT_BT_WINDOWS) << "UUID" << uuid << "KEY" << Qt::hex << key << "TYPE" << Qt::dec << type << "SEQUENCE ERROR";
288 }
289 } else {
290 qCDebug(QT_BT_WINDOWS) << "UUID" << uuid << "KEY" << Qt::hex << key << "TYPE" << Qt::dec << type;
291 }
292 hr = iterator->MoveNext(&current);
293 }
294 // Windows is only able to discover Rfcomm services but the according protocolDescriptor is
295 // not always set in the raw attribute map. If we encounter a service like that we should
296 // fill the protocol descriptor ourselves.
297 if (info.protocolDescriptor(QBluetoothUuid::ProtocolUuid::Rfcomm).isEmpty()) {
298 QBluetoothServiceInfo::Sequence protocolDescriptorList;
299 QBluetoothServiceInfo::Sequence protocol;
300 protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ProtocolUuid::Rfcomm))
301 << QVariant::fromValue(0);
302 protocolDescriptorList.append(QVariant::fromValue(protocol));
303 info.setAttribute(QBluetoothServiceInfo::ProtocolDescriptorList, protocolDescriptorList);
304 }
305 emit serviceFound(address, info);
306 }
307 emit scanFinished(address);
308 deleteLater();
309}
310
311QBluetoothServiceInfo::Sequence QWinRTBluetoothServiceDiscoveryWorker::readSequence(ComPtr<IDataReader> dataReader, bool *ok, quint8 *bytesRead, int depth)
312{
313 if (ok)
314 *ok = false;
315 if (bytesRead)
316 *bytesRead = 0;
317 QBluetoothServiceInfo::Sequence result;
318 if (!dataReader)
319 return result;
320
321 if (depth > QBluetoothServiceDiscoveryAgentPrivate::kMaxSdpRecursionDepth) {
322 qCWarning(QT_BT_WINDOWS) << "SDP sequence recursion depth exceeded";
323 return result;
324 }
325
326 quint8 remainingLength;
327
328 auto verifyLength = [&remainingLength](size_t dataLen) -> bool {
329 if (size_t(remainingLength) < dataLen) {
330 qCWarning(QT_BT_WINDOWS)
331 << "Remaining length of the sequence is not enough to fit the new value."
332 << "Remaining length:" << remainingLength << "Size to read:" << dataLen;
333 return false;
334 }
335 return true;
336 };
337
338#define RETURN_IF_INVALID_LENGTH(dataLen)
339 if (!verifyLength(dataLen)) {
340 result.clear();
341 return result;
342 }
343
344 HRESULT hr = dataReader->ReadByte(&remainingLength);
345 RETURN_IF_FAILED("remainingLength", return result);
346 if (bytesRead)
347 *bytesRead += 1;
348 BYTE type;
349 RETURN_IF_INVALID_LENGTH(sizeof(type));
350 hr = dataReader->ReadByte(&type);
351 RETURN_IF_FAILED("type", return result);
352 remainingLength -= 1;
353 if (bytesRead)
354 *bytesRead += 1;
355 while (true) {
356 switch (type) {
357 case TYPE_UINT8: {
358 quint8 value;
359 RETURN_IF_INVALID_LENGTH(sizeof(value));
360 hr = dataReader->ReadByte(&value);
361 RETURN_IF_FAILED("uint8 value", return result);
362 result.append(QVariant::fromValue(value));
363 remainingLength -= 1;
364 if (bytesRead)
365 *bytesRead += 1;
366 break;
367 }
368 case TYPE_UINT16: {
369 quint16 value;
370 RETURN_IF_INVALID_LENGTH(sizeof(value));
371 hr = dataReader->ReadUInt16(&value);
372 RETURN_IF_FAILED("uint16 value", return result);
373 result.append(QVariant::fromValue(value));
374 remainingLength -= 2;
375 if (bytesRead)
376 *bytesRead += 2;
377 break;
378 }
379 case TYPE_UINT32: {
380 quint32 value;
381 RETURN_IF_INVALID_LENGTH(sizeof(value));
382 hr = dataReader->ReadUInt32(&value);
383 RETURN_IF_FAILED("uint32 value", return result);
384 result.append(QVariant::fromValue(value));
385 remainingLength -= 4;
386 if (bytesRead)
387 *bytesRead += 4;
388 break;
389 }
390 case TYPE_SHORT_UUID: {
391 quint16 b;
392 RETURN_IF_INVALID_LENGTH(sizeof(b));
393 hr = dataReader->ReadUInt16(&b);
394 RETURN_IF_FAILED("short uuid", return result);
395
396 const QBluetoothUuid uuid(b);
397 result.append(QVariant::fromValue(uuid));
398 remainingLength -= 2;
399 if (bytesRead)
400 *bytesRead += 2;
401 break;
402 }
403 case TYPE_LONG_UUID: {
404 GUID b;
405 RETURN_IF_INVALID_LENGTH(sizeof(b));
406 hr = dataReader->ReadGuid(&b);
407 RETURN_IF_FAILED("long uuid", return result);
408 // The latter 8 bytes are in reverse order
409 reverseArray(b.Data4, sizeof(b.Data4)/sizeof(b.Data4[0]));
410 const QBluetoothUuid uuid(b);
411 result.append(QVariant::fromValue(uuid));
412 remainingLength -= sizeof(GUID);
413 if (bytesRead)
414 *bytesRead += sizeof(GUID);
415 break;
416 }
417 case TYPE_STRING: {
418 BYTE length;
419 RETURN_IF_INVALID_LENGTH(sizeof(length));
420 hr = dataReader->ReadByte(&length);
421 RETURN_IF_FAILED("string length", return result);
422 remainingLength -= 1;
423 if (bytesRead)
424 *bytesRead += 1;
425 RETURN_IF_INVALID_LENGTH(size_t(length));
426 HString value;
427 hr = dataReader->ReadString(length, value.GetAddressOf());
428 RETURN_IF_FAILED("string value", return result);
429
430 const QString str = QString::fromWCharArray(WindowsGetStringRawBuffer(value.Get(), nullptr));
431 result.append(QVariant::fromValue(str));
432 remainingLength -= length;
433 if (bytesRead)
434 *bytesRead += length;
435 break;
436 }
437 case TYPE_SEQUENCE: {
438 quint8 bytesR;
439 const QBluetoothServiceInfo::Sequence sequence = readSequence(dataReader, ok, &bytesR, depth + 1);
440 RETURN_IF_INVALID_LENGTH(size_t(bytesR));
441 if (*ok)
442 result.append(QVariant::fromValue(sequence));
443 else
444 return result;
445 remainingLength -= bytesR;
446 if (bytesRead)
447 *bytesRead += bytesR;
448 break;
449 }
450 default:
451 qCDebug(QT_BT_WINDOWS) << "SEQUENCE ERROR" << type;
452 result.clear();
453 return result;
454 }
455 if (remainingLength == 0)
456 break;
457
458 RETURN_IF_INVALID_LENGTH(sizeof(type));
459 hr = dataReader->ReadByte(&type);
460 RETURN_IF_FAILED("next type", return result);
461 remainingLength -= 1;
462 if (bytesRead)
463 *bytesRead += 1;
464 }
465
466#undef RETURN_IF_INVALID_LENGTH
467
468 if (ok)
469 *ok = true;
470 return result;
471}
472
473QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(
474 QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &deviceAdapter)
475 : error(QBluetoothServiceDiscoveryAgent::NoError),
476 state(Inactive),
477 mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery),
478 singleDevice(false),
479 q_ptr(qp)
480{
481 threadCoInit(this);
482 // TODO: use local adapter for discovery. Possible?
483 Q_UNUSED(deviceAdapter);
484}
485
486QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate()
487{
488 releaseWorker();
489 threadCoUninit(this);
490}
491
492void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &address)
493{
494 if (worker)
495 return;
496
497 worker = new QWinRTBluetoothServiceDiscoveryWorker(address.toUInt64(), mode);
498
499 connect(worker, &QWinRTBluetoothServiceDiscoveryWorker::serviceFound,
500 this, &QBluetoothServiceDiscoveryAgentPrivate::processFoundService, Qt::QueuedConnection);
501 connect(worker, &QWinRTBluetoothServiceDiscoveryWorker::scanFinished,
502 this, &QBluetoothServiceDiscoveryAgentPrivate::onScanFinished, Qt::QueuedConnection);
503 connect(worker, &QWinRTBluetoothServiceDiscoveryWorker::errorOccured,
504 this, &QBluetoothServiceDiscoveryAgentPrivate::onError, Qt::QueuedConnection);
505 worker->start();
506}
507
508void QBluetoothServiceDiscoveryAgentPrivate::stop()
509{
510 releaseWorker();
511 Q_Q(QBluetoothServiceDiscoveryAgent);
512 emit q->canceled();
513}
514
515void QBluetoothServiceDiscoveryAgentPrivate::processFoundService(quint64 deviceAddress, const QBluetoothServiceInfo &info)
516{
517 Q_Q(QBluetoothServiceDiscoveryAgent);
518 //apply uuidFilter
519 if (!uuidFilter.isEmpty()) {
520 bool serviceNameMatched = uuidFilter.contains(info.serviceUuid());
521 bool serviceClassMatched = false;
522 const QList<QBluetoothUuid> serviceClassUuids
523 = info.serviceClassUuids();
524 for (const QBluetoothUuid &id : serviceClassUuids) {
525 if (uuidFilter.contains(id)) {
526 serviceClassMatched = true;
527 break;
528 }
529 }
530
531 if (!serviceNameMatched && !serviceClassMatched)
532 return;
533 }
534
535 if (!info.isValid())
536 return;
537
538 QBluetoothServiceInfo returnInfo(info);
539 bool deviceFound;
540 for (const QBluetoothDeviceInfo &deviceInfo : std::as_const(discoveredDevices)) {
541 if (deviceInfo.address().toUInt64() == deviceAddress) {
542 deviceFound = true;
543 returnInfo.setDevice(deviceInfo);
544 break;
545 }
546 }
547 Q_ASSERT(deviceFound);
548
549 if (!isDuplicatedService(returnInfo)) {
550 discoveredServices.append(returnInfo);
551 qCDebug(QT_BT_WINDOWS) << "Discovered services" << discoveredDevices.at(0).address().toString()
552 << returnInfo.serviceName() << returnInfo.serviceUuid()
553 << ">>>" << returnInfo.serviceClassUuids();
554
555 emit q->serviceDiscovered(returnInfo);
556 }
557}
558
559void QBluetoothServiceDiscoveryAgentPrivate::onScanFinished(quint64 deviceAddress)
560{
561 // The scan for a device's services has finished. Disconnect the
562 // worker and call the baseclass function which starts the scan for
563 // the next device if there are any unscanned devices left (or finishes
564 // the scan if none left)
565 releaseWorker();
566 _q_serviceDiscoveryFinished();
567}
568
569void QBluetoothServiceDiscoveryAgentPrivate::onError()
570{
571 Q_Q(QBluetoothServiceDiscoveryAgent);
572 discoveredDevices.clear();
573 error = QBluetoothServiceDiscoveryAgent::InputOutputError;
574 errorString = QStringLiteral("errorDescription");
575 emit q->errorOccurred(error);
576}
577
578void QBluetoothServiceDiscoveryAgentPrivate::releaseWorker()
579{
580 if (!worker)
581 return;
582
583 disconnect(worker, &QWinRTBluetoothServiceDiscoveryWorker::serviceFound,
584 this, &QBluetoothServiceDiscoveryAgentPrivate::processFoundService);
585 disconnect(worker, &QWinRTBluetoothServiceDiscoveryWorker::scanFinished,
586 this, &QBluetoothServiceDiscoveryAgentPrivate::onScanFinished);
587 disconnect(worker, &QWinRTBluetoothServiceDiscoveryWorker::errorOccured,
588 this, &QBluetoothServiceDiscoveryAgentPrivate::onError);
589 // mWorker will delete itself as soon as it is done with its discovery
590 worker = nullptr;
591}
592
593QT_END_NAMESPACE
594
595#include <qbluetoothservicediscoveryagent_winrt.moc>
\inmodule QtBluetooth
void scanFinished(quint64 deviceAddress)
Collections::IIterator< ValueItem * > ValueIterator
#define RETURN_IF_INVALID_LENGTH(dataLen)
Collections::IKeyValuePair< UINT32, IBuffer * > ValueItem
static void reverseArray(uchar data[], size_t length)
Collections::IIterable< ValueItem * > ValueIterable
QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcQIORing)