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 device->get_BluetoothAddress(&address);
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)
131 if (status != Completed) {
132 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain service list";
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);
146 Q_ASSERT_SUCCEEDED(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);
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);
169 hr = host->get_RawName(hostName.GetAddressOf());
170 Q_ASSERT_SUCCEEDED(hr);
171 const QString qHostName = QString::fromWCharArray(WindowsGetStringRawBuffer(hostName.Get(),
173 ComPtr<IRfcommServiceId> id;
174 hr = service->get_ServiceId(&id);
175 Q_ASSERT_SUCCEEDED(hr);
177 hr = id->get_Uuid(&guid);
178 const QBluetoothUuid uuid(guid);
179 Q_ASSERT_SUCCEEDED(hr);
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());
190 qCDebug(QT_BT_WINDOWS) <<
"Check manifest capabilities";
193 ComPtr<IMapView<UINT32, IBuffer *>> mapView;
194 hr = QWinRTFunctions::await(op, mapView.GetAddressOf());
195 Q_ASSERT_SUCCEEDED(hr);
197 ComPtr<ValueIterable> iterable;
198 ComPtr<ValueIterator> iterator;
200 hr = mapView.As(&iterable);
204 boolean current =
false;
205 hr = iterable->First(&iterator);
208 hr = iterator->get_HasCurrent(¤t);
212 while (SUCCEEDED(hr) && current) {
213 ComPtr<ValueItem> item;
214 hr = iterator->get_Current(&item);
219 hr = item->get_Key(&key);
223 ComPtr<IBuffer> buffer;
224 hr = item->get_Value(&buffer);
225 Q_ASSERT_SUCCEEDED(hr);
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);
234 hr = dataReader->ReadByte(&type);
238 hr = dataReader->ReadByte(&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;
244 hr = dataReader->ReadUInt16(&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;
250 hr = dataReader->ReadUInt32(&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;
256 hr = dataReader->ReadUInt16(&value);
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;
263 hr = dataReader->ReadGuid(&value);
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;
272 hr = dataReader->ReadByte(&length);
275 hr = dataReader->ReadString(length, value.GetAddressOf());
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;
282 QBluetoothServiceInfo::Sequence sequence = readSequence(dataReader, &ok,
nullptr);
284 info.setAttribute(key, sequence);
285 qCDebug(QT_BT_WINDOWS) <<
"UUID" << uuid <<
"KEY" << Qt::hex << key <<
"TYPE" << Qt::dec << type <<
"SEQUENCE" << sequence;
287 qCDebug(QT_BT_WINDOWS) <<
"UUID" << uuid <<
"KEY" << Qt::hex << key <<
"TYPE" << Qt::dec << type <<
"SEQUENCE ERROR";
290 qCDebug(QT_BT_WINDOWS) <<
"UUID" << uuid <<
"KEY" << Qt::hex << key <<
"TYPE" << Qt::dec << type;
292 hr = iterator->MoveNext(¤t);
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);
305 emit serviceFound(address, info);
307 emit scanFinished(address);
317 QBluetoothServiceInfo::Sequence result;
321 if (depth > QBluetoothServiceDiscoveryAgentPrivate::kMaxSdpRecursionDepth) {
322 qCWarning(QT_BT_WINDOWS) <<
"SDP sequence recursion depth exceeded";
326 quint8 remainingLength;
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;
338#define RETURN_IF_INVALID_LENGTH(dataLen)
339 if (!verifyLength(dataLen)) {
344 HRESULT hr = dataReader->ReadByte(&remainingLength);
345 RETURN_IF_FAILED(
"remainingLength",
return result);
350 hr = dataReader->ReadByte(&type);
351 RETURN_IF_FAILED(
"type",
return result);
352 remainingLength -= 1;
360 hr = dataReader->ReadByte(&value);
361 RETURN_IF_FAILED(
"uint8 value",
return result);
362 result.append(QVariant::fromValue(value));
363 remainingLength -= 1;
371 hr = dataReader->ReadUInt16(&value);
372 RETURN_IF_FAILED(
"uint16 value",
return result);
373 result.append(QVariant::fromValue(value));
374 remainingLength -= 2;
382 hr = dataReader->ReadUInt32(&value);
383 RETURN_IF_FAILED(
"uint32 value",
return result);
384 result.append(QVariant::fromValue(value));
385 remainingLength -= 4;
393 hr = dataReader->ReadUInt16(&b);
394 RETURN_IF_FAILED(
"short uuid",
return result);
396 const QBluetoothUuid uuid(b);
397 result.append(QVariant::fromValue(uuid));
398 remainingLength -= 2;
406 hr = dataReader->ReadGuid(&b);
407 RETURN_IF_FAILED(
"long uuid",
return result);
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);
414 *bytesRead +=
sizeof(GUID);
420 hr = dataReader->ReadByte(&length);
421 RETURN_IF_FAILED(
"string length",
return result);
422 remainingLength -= 1;
427 hr = dataReader->ReadString(length, value.GetAddressOf());
428 RETURN_IF_FAILED(
"string value",
return result);
430 const QString str = QString::fromWCharArray(WindowsGetStringRawBuffer(value.Get(),
nullptr));
431 result.append(QVariant::fromValue(str));
432 remainingLength -= length;
434 *bytesRead += length;
439 const QBluetoothServiceInfo::Sequence sequence = readSequence(dataReader, ok, &bytesR, depth + 1);
442 result.append(QVariant::fromValue(sequence));
445 remainingLength -= bytesR;
447 *bytesRead += bytesR;
451 qCDebug(QT_BT_WINDOWS) <<
"SEQUENCE ERROR" << type;
455 if (remainingLength == 0)
459 hr = dataReader->ReadByte(&type);
460 RETURN_IF_FAILED(
"next type",
return result);
461 remainingLength -= 1;
466#undef RETURN_IF_INVALID_LENGTH
473QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(
474 QBluetoothServiceDiscoveryAgent *qp,
const QBluetoothAddress &deviceAdapter)
475 : error(QBluetoothServiceDiscoveryAgent::NoError),
477 mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery),
483 Q_UNUSED(deviceAdapter);
486QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate()
489 threadCoUninit(
this);
492void QBluetoothServiceDiscoveryAgentPrivate::start(
const QBluetoothAddress &address)
497 worker =
new QWinRTBluetoothServiceDiscoveryWorker(address.toUInt64(), mode);
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);
508void QBluetoothServiceDiscoveryAgentPrivate::stop()
511 Q_Q(QBluetoothServiceDiscoveryAgent);
515void QBluetoothServiceDiscoveryAgentPrivate::processFoundService(quint64 deviceAddress,
const QBluetoothServiceInfo &info)
517 Q_Q(QBluetoothServiceDiscoveryAgent);
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;
531 if (!serviceNameMatched && !serviceClassMatched)
538 QBluetoothServiceInfo returnInfo(info);
540 for (
const QBluetoothDeviceInfo &deviceInfo : std::as_const(discoveredDevices)) {
541 if (deviceInfo.address().toUInt64() == deviceAddress) {
543 returnInfo.setDevice(deviceInfo);
547 Q_ASSERT(deviceFound);
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();
555 emit q->serviceDiscovered(returnInfo);
559void QBluetoothServiceDiscoveryAgentPrivate::onScanFinished(quint64 deviceAddress)
566 _q_serviceDiscoveryFinished();
569void QBluetoothServiceDiscoveryAgentPrivate::onError()
571 Q_Q(QBluetoothServiceDiscoveryAgent);
572 discoveredDevices.clear();
573 error = QBluetoothServiceDiscoveryAgent::InputOutputError;
574 errorString = QStringLiteral(
"errorDescription");
575 emit q->errorOccurred(error);
578void QBluetoothServiceDiscoveryAgentPrivate::releaseWorker()
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);
595#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)