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