9#include <QtCore/QLoggingCategory>
10#include <QtCore/private/qfunctions_winrt_p.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>
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;
42#define TYPE_SHORT_UUID 25
43#define TYPE_LONG_UUID 28
45#define TYPE_SEQUENCE 53
47#define BREAK_IF_FAILED(msg) RETURN_IF_FAILED(msg, break)
52 for (size_t i = length; i > length/2; i--)
53 std::swap(data[length - i], data[i - 1]);
71 HRESULT onBluetoothDeviceFoundAsync(IAsyncOperation<BluetoothDevice *> *op, AsyncStatus status);
73 void processServiceSearchResult(quint64 address, ComPtr<IVectorView<RfcommDeviceService*>> services);
77 quint64 m_targetAddress;
82 QBluetoothServiceDiscoveryAgent::DiscoveryMode mode)
83 : m_targetAddress(targetAddress)
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);
107 if (status != Completed) {
108 qCDebug(QT_BT_WINDOWS) <<
"Could not find device";
113 ComPtr<IBluetoothDevice> device;
115 hr = op->GetResults(&device);
116 Q_ASSERT_SUCCEEDED(hr);
118 qCDebug(QT_BT_WINDOWS) <<
"The returned device is NULL";
123 device->get_BluetoothAddress(&address);
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)
136 if (status != Completed) {
137 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain service list";
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);
151 Q_ASSERT_SUCCEEDED(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);
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);
174 hr = host->get_RawName(hostName.GetAddressOf());
175 Q_ASSERT_SUCCEEDED(hr);
176 const QString qHostName = QString::fromWCharArray(WindowsGetStringRawBuffer(hostName.Get(),
178 ComPtr<IRfcommServiceId> id;
179 hr = service->get_ServiceId(&id);
180 Q_ASSERT_SUCCEEDED(hr);
182 hr = id->get_Uuid(&guid);
183 const QBluetoothUuid uuid(guid);
184 Q_ASSERT_SUCCEEDED(hr);
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());
195 qCDebug(QT_BT_WINDOWS) <<
"Check manifest capabilities";
198 ComPtr<IMapView<UINT32, IBuffer *>> mapView;
199 hr = QWinRTFunctions::await(op, mapView.GetAddressOf());
200 Q_ASSERT_SUCCEEDED(hr);
202 ComPtr<ValueIterable> iterable;
203 ComPtr<ValueIterator> iterator;
205 hr = mapView.As(&iterable);
209 boolean current =
false;
210 hr = iterable->First(&iterator);
213 hr = iterator->get_HasCurrent(¤t);
217 while (SUCCEEDED(hr) && current) {
218 ComPtr<ValueItem> item;
219 hr = iterator->get_Current(&item);
224 hr = item->get_Key(&key);
228 ComPtr<IBuffer> buffer;
229 hr = item->get_Value(&buffer);
230 Q_ASSERT_SUCCEEDED(hr);
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);
239 hr = dataReader->ReadByte(&type);
243 hr = dataReader->ReadByte(&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;
249 hr = dataReader->ReadUInt16(&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;
255 hr = dataReader->ReadUInt32(&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;
261 hr = dataReader->ReadUInt16(&value);
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;
268 hr = dataReader->ReadGuid(&value);
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;
277 hr = dataReader->ReadByte(&length);
280 hr = dataReader->ReadString(length, value.GetAddressOf());
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;
287 QBluetoothServiceInfo::Sequence sequence = readSequence(dataReader, &ok,
nullptr);
289 info.setAttribute(key, sequence);
290 qCDebug(QT_BT_WINDOWS) <<
"UUID" << uuid <<
"KEY" << Qt::hex << key <<
"TYPE" << Qt::dec << type <<
"SEQUENCE" << sequence;
292 qCDebug(QT_BT_WINDOWS) <<
"UUID" << uuid <<
"KEY" << Qt::hex << key <<
"TYPE" << Qt::dec << type <<
"SEQUENCE ERROR";
295 qCDebug(QT_BT_WINDOWS) <<
"UUID" << uuid <<
"KEY" << Qt::hex << key <<
"TYPE" << Qt::dec << type;
297 hr = iterator->MoveNext(¤t);
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);
310 emit serviceFound(address, info);
312 emit scanFinished(address);
322 QBluetoothServiceInfo::Sequence result;
326 if (depth > QBluetoothServiceDiscoveryAgentPrivate::kMaxSdpRecursionDepth) {
327 qCWarning(QT_BT_WINDOWS) <<
"SDP sequence recursion depth exceeded";
331 quint8 remainingLength;
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;
343#define RETURN_IF_INVALID_LENGTH(dataLen)
344 if (!verifyLength(dataLen)) {
349 HRESULT hr = dataReader->ReadByte(&remainingLength);
350 RETURN_IF_FAILED(
"remainingLength",
return result);
355 hr = dataReader->ReadByte(&type);
356 RETURN_IF_FAILED(
"type",
return result);
357 remainingLength -= 1;
365 hr = dataReader->ReadByte(&value);
366 RETURN_IF_FAILED(
"uint8 value",
return result);
367 result.append(QVariant::fromValue(value));
368 remainingLength -= 1;
376 hr = dataReader->ReadUInt16(&value);
377 RETURN_IF_FAILED(
"uint16 value",
return result);
378 result.append(QVariant::fromValue(value));
379 remainingLength -= 2;
387 hr = dataReader->ReadUInt32(&value);
388 RETURN_IF_FAILED(
"uint32 value",
return result);
389 result.append(QVariant::fromValue(value));
390 remainingLength -= 4;
398 hr = dataReader->ReadUInt16(&b);
399 RETURN_IF_FAILED(
"short uuid",
return result);
401 const QBluetoothUuid uuid(b);
402 result.append(QVariant::fromValue(uuid));
403 remainingLength -= 2;
411 hr = dataReader->ReadGuid(&b);
412 RETURN_IF_FAILED(
"long uuid",
return result);
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);
419 *bytesRead +=
sizeof(GUID);
425 hr = dataReader->ReadByte(&length);
426 RETURN_IF_FAILED(
"string length",
return result);
427 remainingLength -= 1;
432 hr = dataReader->ReadString(length, value.GetAddressOf());
433 RETURN_IF_FAILED(
"string value",
return result);
435 const QString str = QString::fromWCharArray(WindowsGetStringRawBuffer(value.Get(),
nullptr));
436 result.append(QVariant::fromValue(str));
437 remainingLength -= length;
439 *bytesRead += length;
444 const QBluetoothServiceInfo::Sequence sequence = readSequence(dataReader, ok, &bytesR, depth + 1);
447 result.append(QVariant::fromValue(sequence));
450 remainingLength -= bytesR;
452 *bytesRead += bytesR;
456 qCDebug(QT_BT_WINDOWS) <<
"SEQUENCE ERROR" << type;
460 if (remainingLength == 0)
464 hr = dataReader->ReadByte(&type);
465 RETURN_IF_FAILED(
"next type",
return result);
466 remainingLength -= 1;
471#undef RETURN_IF_INVALID_LENGTH
478QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(
479 QBluetoothServiceDiscoveryAgent *qp,
const QBluetoothAddress &deviceAdapter)
480 : error(QBluetoothServiceDiscoveryAgent::NoError),
482 mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery),
488 Q_UNUSED(deviceAdapter);
491QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate()
494 threadCoUninit(
this);
497void QBluetoothServiceDiscoveryAgentPrivate::start(
const QBluetoothAddress &address)
502 worker =
new QWinRTBluetoothServiceDiscoveryWorker(address.toUInt64(), mode);
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);
513void QBluetoothServiceDiscoveryAgentPrivate::stop()
516 Q_Q(QBluetoothServiceDiscoveryAgent);
520void QBluetoothServiceDiscoveryAgentPrivate::processFoundService(quint64 deviceAddress,
const QBluetoothServiceInfo &info)
522 Q_Q(QBluetoothServiceDiscoveryAgent);
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;
536 if (!serviceNameMatched && !serviceClassMatched)
543 QBluetoothServiceInfo returnInfo(info);
545 for (
const QBluetoothDeviceInfo &deviceInfo : std::as_const(discoveredDevices)) {
546 if (deviceInfo.address().toUInt64() == deviceAddress) {
548 returnInfo.setDevice(deviceInfo);
552 Q_ASSERT(deviceFound);
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();
560 emit q->serviceDiscovered(returnInfo);
564void QBluetoothServiceDiscoveryAgentPrivate::onScanFinished(quint64 deviceAddress)
571 _q_serviceDiscoveryFinished();
574void QBluetoothServiceDiscoveryAgentPrivate::onError()
576 Q_Q(QBluetoothServiceDiscoveryAgent);
577 discoveredDevices.clear();
578 error = QBluetoothServiceDiscoveryAgent::InputOutputError;
579 errorString = QStringLiteral(
"errorDescription");
580 emit q->errorOccurred(error);
583void QBluetoothServiceDiscoveryAgentPrivate::releaseWorker()
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);
600#include <qbluetoothservicediscoveryagent_winrt.moc>
~QWinRTBluetoothServiceDiscoveryWorker()
void scanFinished(quint64 deviceAddress)
#define BREAK_IF_FAILED(msg)
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)