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