Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qlowenergycontroller_winrt.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 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
6
7#include <QtBluetooth/qbluetoothlocaldevice.h>
8#include <QtBluetooth/QLowEnergyCharacteristicData>
9#include <QtBluetooth/QLowEnergyDescriptorData>
10#include <QtBluetooth/private/qbluetoothutils_winrt_p.h>
11#include <QtBluetooth/QLowEnergyService>
12
13#include <QtCore/QtEndian>
14#include <QtCore/QLoggingCategory>
15#include <QtCore/private/qfunctions_winrt_p.h>
16#include <QtCore/QDeadlineTimer>
17
18#include <functional>
19#include <robuffer.h>
20#include <windows.devices.enumeration.h>
21#include <windows.devices.bluetooth.h>
22#include <windows.devices.bluetooth.genericattributeprofile.h>
23#include <windows.foundation.collections.h>
24#include <windows.foundation.metadata.h>
25#include <windows.storage.streams.h>
26
27using namespace Microsoft::WRL;
28using namespace Microsoft::WRL::Wrappers;
29using namespace ABI::Windows::Foundation;
30using namespace ABI::Windows::Foundation::Collections;
31using namespace ABI::Windows::Foundation::Metadata;
32using namespace ABI::Windows::Devices;
34using namespace ABI::Windows::Devices::Bluetooth::GenericAttributeProfile;
35using namespace ABI::Windows::Devices::Enumeration;
36using namespace ABI::Windows::Storage::Streams;
37
39
40typedef ITypedEventHandler<BluetoothLEDevice *, IInspectable *> StatusHandler;
41typedef ITypedEventHandler<GattSession *, IInspectable *> MtuHandler;
42typedef ITypedEventHandler<GattCharacteristic *, GattValueChangedEventArgs *> ValueChangedHandler;
43typedef GattReadClientCharacteristicConfigurationDescriptorResult ClientCharConfigDescriptorResult;
44typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharConfigDescriptorResult;
45
46#define EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr) \
47 if (FAILED(hr)) { \
48 emitErrorAndQuitThread(hr); \
49 return; \
50 }
51
52#define EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, message) \
53 if (FAILED(hr)) { \
54 emitErrorAndQuitThread(message); \
55 return; \
56 }
57
58#define WARN_AND_CONTINUE_IF_FAILED(hr, msg) \
59 if (FAILED(hr)) { \
60 qCWarning(QT_BT_WINDOWS) << msg; \
61 continue; \
62 }
63
64#define DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr, msg) \
65 if (FAILED(hr)) { \
66 qCWarning(QT_BT_WINDOWS) << msg; \
67 --mCharacteristicsCountToBeDiscovered; \
68 continue; \
69 }
70
71#define CHECK_FOR_DEVICE_CONNECTION_ERROR_IMPL(this, hr, msg, ret) \
72 if (FAILED(hr)) { \
73 this->handleConnectionError(msg); \
74 ret; \
75 }
76
77#define CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, msg, ret) \
78 CHECK_FOR_DEVICE_CONNECTION_ERROR_IMPL(this, hr, msg, ret)
79
80#define CHECK_HR_AND_SET_SERVICE_ERROR(hr, msg, service, error, ret) \
81 if (FAILED(hr)) { \
82 qCDebug(QT_BT_WINDOWS) << msg; \
83 service->setError(error); \
84 ret; \
85 }
86
87Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
88Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS_SERVICE_THREAD)
89
90static constexpr qint64 kMaxConnectTimeout = 20000; // 20 sec
91
92static QByteArray byteArrayFromGattResult(const ComPtr<IGattReadResult> &gattResult,
93 bool isWCharString = false)
94{
96 HRESULT hr;
97 hr = gattResult->get_Value(&buffer);
98 if (FAILED(hr) || !buffer) {
99 qCWarning(QT_BT_WINDOWS) << "Could not obtain buffer from GattReadResult";
100 return QByteArray();
101 }
102 return byteArrayFromBuffer(buffer, isWCharString);
103}
104
105template <typename T>
106static void closeDeviceService(ComPtr<T> service)
107{
108 ComPtr<IClosable> closableService;
109 HRESULT hr = service.As(&closableService);
110 RETURN_IF_FAILED("Could not cast type to closable", return);
111 hr = closableService->Close();
112 RETURN_IF_FAILED("Close() call failed", return);
113}
114
116{
118public:
120 const ComPtr<IGattDeviceService3> &deviceService,
122 : mService(service), mMode(mode), mDeviceService(deviceService)
123 {
124 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
125 }
126
128 {
129 if (mAbortRequested)
131 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
132 }
133
134public slots:
136 {
137 auto exitCondition = [this]() { return mAbortRequested; };
138
140 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
142 ComPtr<IGattCharacteristicsResult> characteristicsResult;
143 HRESULT hr = mDeviceService->GetCharacteristicsAsync(&characteristicsOp);
145 hr = QWinRTFunctions::await(characteristicsOp, characteristicsResult.GetAddressOf(),
146 QWinRTFunctions::ProcessMainThreadEvents, 5000,
147 exitCondition);
149 GattCommunicationStatus status;
150 hr = characteristicsResult->get_Status(&status);
152 if (status != GattCommunicationStatus_Success) {
153 emitErrorAndQuitThread(QLatin1String("Could not obtain char list"));
154 return;
155 }
157 hr = characteristicsResult->get_Characteristics(&characteristics);
159
160 uint characteristicsCount;
161 hr = characteristics->get_Size(&characteristicsCount);
163
164 mCharacteristicsCountToBeDiscovered = characteristicsCount;
165 for (uint i = 0; !mAbortRequested && (i < characteristicsCount); ++i) {
166 ComPtr<IGattCharacteristic> characteristic;
167 hr = characteristics->GetAt(i, &characteristic);
168 if (FAILED(hr)) {
169 qCWarning(QT_BT_WINDOWS) << "Could not obtain characteristic at" << i;
171 continue;
172 }
173
174 ComPtr<IGattCharacteristic3> characteristic3;
175 hr = characteristic.As(&characteristic3);
176 if (FAILED(hr)) {
177 qCWarning(QT_BT_WINDOWS) << "Could not cast characteristic";
179 continue;
180 }
181
182 // For some strange reason, Windows doesn't discover descriptors of characteristics (if not paired).
183 // Qt API assumes that all characteristics and their descriptors are discovered in one go.
184 // So we start 'GetDescriptorsAsync' for each discovered characteristic and finish only
185 // when GetDescriptorsAsync for all characteristics return.
187 hr = characteristic3->GetDescriptorsAsync(&descAsyncOp);
188 DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr, "Could not obtain list of descriptors")
189
191 hr = QWinRTFunctions::await(descAsyncOp, descResult.GetAddressOf(),
192 QWinRTFunctions::ProcessMainThreadEvents, 5000,
193 exitCondition);
194 DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor read result")
195
197 hr = characteristic->get_AttributeHandle(&handle);
199 hr, "Could not obtain characteristic's attribute handle")
201 charData.valueHandle = handle + 1;
202 if (mStartHandle == 0 || mStartHandle > handle)
204 if (mEndHandle == 0 || mEndHandle < handle)
206 GUID guuid;
207 hr = characteristic->get_Uuid(&guuid);
208 DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic's Uuid")
209 charData.uuid = QBluetoothUuid(guuid);
210 GattCharacteristicProperties properties;
211 hr = characteristic->get_CharacteristicProperties(&properties);
213 "Could not obtain characteristic's properties")
214 charData.properties = QLowEnergyCharacteristic::PropertyTypes(properties & 0xff);
218 hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached,
219 &readOp);
220 DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr, "Could not read characteristic")
221 ComPtr<IGattReadResult> readResult;
222 hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf(),
223 QWinRTFunctions::ProcessMainThreadEvents, 5000,
224 exitCondition);
226 "Could not obtain characteristic read result")
227 if (!readResult)
228 qCWarning(QT_BT_WINDOWS) << "Characteristic read result is null";
229 else
230 charData.value = byteArrayFromGattResult(readResult);
231 }
233
235
236 GattCommunicationStatus commStatus;
237 hr = descResult->get_Status(&commStatus);
238 if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
239 qCWarning(QT_BT_WINDOWS) << "Descriptor operation failed";
241 continue;
242 }
243
244 hr = descResult->get_Descriptors(&descriptors);
245 DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr, "Could not obtain list of descriptors")
246
247 uint descriptorCount;
248 hr = descriptors->get_Size(&descriptorCount);
249 DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr, "Could not obtain list of descriptors' size")
250 for (uint j = 0; !mAbortRequested && (j < descriptorCount); ++j) {
252 ComPtr<IGattDescriptor> descriptor;
253 hr = descriptors->GetAt(j, &descriptor);
254 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor")
255 quint16 descHandle;
256 hr = descriptor->get_AttributeHandle(&descHandle);
257 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor's attribute handle")
258 GUID descriptorUuid;
259 hr = descriptor->get_Uuid(&descriptorUuid);
260 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor's Uuid")
261 descData.uuid = QBluetoothUuid(descriptorUuid);
262 charData.descriptorList.insert(descHandle, descData);
266 hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(
267 &readOp);
268 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not read descriptor value")
270 hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf(),
271 QWinRTFunctions::ProcessMainThreadEvents, 5000,
272 exitCondition);
273 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not await descriptor read result")
274 GattClientCharacteristicConfigurationDescriptorValue value;
275 hr = readResult->get_ClientCharacteristicConfigurationDescriptor(&value);
277 "Could not get descriptor value from result")
278 quint16 result = 0;
279 bool correct = false;
280 if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) {
281 result |= GattClientCharacteristicConfigurationDescriptorValue_Indicate;
282 correct = true;
283 }
284 if (value & GattClientCharacteristicConfigurationDescriptorValue_Notify) {
285 result |= GattClientCharacteristicConfigurationDescriptorValue_Notify;
286 correct = true;
287 }
288 if (value == GattClientCharacteristicConfigurationDescriptorValue_None)
289 correct = true;
290 if (!correct)
291 continue;
292
293 descData.value = QByteArray(2, Qt::Uninitialized);
294 qToLittleEndian(result, descData.value.data());
295 }
296 mIndicateChars << charData.uuid;
297 } else {
300 hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached,
301 &readOp);
302 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not read descriptor value")
303 ComPtr<IGattReadResult> readResult;
304 hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf(),
305 QWinRTFunctions::ProcessMainThreadEvents, 5000,
306 exitCondition);
307 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not await descriptor read result")
309 descData.value = byteArrayFromGattResult(readResult, true);
310 else
311 descData.value = byteArrayFromGattResult(readResult);
312 }
313 }
314 charData.descriptorList.insert(descHandle, descData);
315 }
316
319 }
320 checkAllCharacteristicsDiscovered();
321 }
322
324 {
325 mAbortRequested = true;
326 }
327
328private:
329 void checkAllCharacteristicsDiscovered();
330 void emitErrorAndQuitThread(HRESULT hr);
331 void emitErrorAndQuitThread(const QString &error);
332
333public:
342 bool mAbortRequested = false;
343
344signals:
345 void charListObtained(const QBluetoothUuid &service,
347 QList<QBluetoothUuid> indicateChars, QLowEnergyHandle startHandle,
348 QLowEnergyHandle endHandle);
350};
351
352void QWinRTLowEnergyServiceHandler::checkAllCharacteristicsDiscovered()
353{
357 }
359}
360
361void QWinRTLowEnergyServiceHandler::emitErrorAndQuitThread(HRESULT hr)
362{
363 emitErrorAndQuitThread(qt_error_string(hr));
364}
365
366void QWinRTLowEnergyServiceHandler::emitErrorAndQuitThread(const QString &error)
367{
368 mAbortRequested = true; // so that the service is closed during cleanup
371}
372
374{
376public:
378 {
379 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
380 // This should be checked before the handler is created
381 Q_ASSERT(!mAddress.isNull());
382 }
384 {
385 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
386 mDevice.Reset();
387 mGattSession.Reset();
388 // To close the COM library gracefully, each successful call to
389 // CoInitialize, including those that return S_FALSE, must be balanced
390 // by a corresponding call to CoUninitialize.
391 if (mInitialized == S_OK || mInitialized == S_FALSE)
392 CoUninitialize();
393 }
394
395public slots:
396 void connectToDevice();
398
399signals:
402
403private:
404 void connectToPairedDevice();
405 void connectToUnpairedDevice();
406 void emitErrorAndQuitThread(const QString &error);
407 void emitErrorAndQuitThread(const char *error);
408 void emitConnectedAndQuitThread();
409
410 ComPtr<IBluetoothLEDevice> mDevice = nullptr;
411 ComPtr<IGattSession> mGattSession = nullptr;
412 const QBluetoothAddress mAddress;
413 bool mAbortConnection = false;
414 HRESULT mInitialized = E_UNEXPECTED; // some error code
415};
416
418{
419 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
420 mInitialized = CoInitializeEx(NULL, COINIT_MULTITHREADED);
421 qCDebug(QT_BT_WINDOWS) << qt_error_string(mInitialized);
422
423 auto earlyExit = [this]() { return mAbortConnection; };
425 HRESULT hr = GetActivationFactory(
426 HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice).Get(),
427 &deviceStatics);
428 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain device factory");
429 ComPtr<IAsyncOperation<BluetoothLEDevice *>> deviceFromIdOperation;
430 hr = deviceStatics->FromBluetoothAddressAsync(mAddress.toUInt64(), &deviceFromIdOperation);
431 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not find LE device from address");
432 hr = QWinRTFunctions::await(deviceFromIdOperation, mDevice.GetAddressOf(),
433 QWinRTFunctions::ProcessMainThreadEvents, 5000, earlyExit);
434 if (FAILED(hr) || !mDevice) {
435 emitErrorAndQuitThread("Could not find LE device");
436 return;
437 }
438
439 // get GattSession: 1. get device id
441 hr = mDevice.As(&device4);
442 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not cast device");
443
445 hr = device4->get_BluetoothDeviceId(&deviceId);
446 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not get bluetooth device id");
447
448 // get GattSession: 2. get session statics
449 ComPtr<IGattSessionStatics> sessionStatics;
450 hr = GetActivationFactory(
451 HString::MakeReference(
452 RuntimeClass_Windows_Devices_Bluetooth_GenericAttributeProfile_GattSession)
453 .Get(),
454 &sessionStatics);
455 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain GattSession statics");
456
457 // get GattSession: 3. get session
458 ComPtr<IAsyncOperation<GattSession *>> gattSessionFromIdOperation;
459 hr = sessionStatics->FromDeviceIdAsync(deviceId.Get(), &gattSessionFromIdOperation);
460 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not get GattSession from id");
461 hr = QWinRTFunctions::await(gattSessionFromIdOperation, mGattSession.GetAddressOf(),
462 QWinRTFunctions::ProcessMainThreadEvents, 5000, earlyExit);
463 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not complete Gatt session acquire");
464
465 BluetoothConnectionStatus status;
466 hr = mDevice->get_ConnectionStatus(&status);
467 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain device's connection status");
468 if (status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) {
469 emitConnectedAndQuitThread();
470 return;
471 }
472
473 QBluetoothLocalDevice localDevice;
474 QBluetoothLocalDevice::Pairing pairing = localDevice.pairingStatus(mAddress);
475 if (pairing == QBluetoothLocalDevice::Unpaired)
476 connectToUnpairedDevice();
477 else
478 connectToPairedDevice();
479}
480
482{
483 mAbortConnection = true;
484 // Disconnect from the QLowEnergyControllerPrivateWinRT, so that it does
485 // not get notifications. It's absolutely fine to keep doing smth in
486 // background, as multiple connections to the same device should be handled
487 // correctly by OS.
490}
491
492void QWinRTLowEnergyConnectionHandler::connectToPairedDevice()
493{
494 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
496 HRESULT hr = mDevice.As(&device3);
497 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not cast device");
499 auto earlyExit = [this]() { return mAbortConnection; };
501 while (!mAbortConnection && !deadline.hasExpired()) {
502 hr = device3->GetGattServicesAsync(&deviceServicesOp);
503 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain services");
504 ComPtr<IGattDeviceServicesResult> deviceServicesResult;
505 hr = QWinRTFunctions::await(deviceServicesOp, deviceServicesResult.GetAddressOf(),
506 QWinRTFunctions::ProcessThreadEvents, 5000, earlyExit);
507 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not await services operation");
508
509 GattCommunicationStatus commStatus;
510 hr = deviceServicesResult->get_Status(&commStatus);
511 if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
512 emitErrorAndQuitThread("Service operation failed");
513 return;
514 }
515
517 hr = deviceServicesResult->get_Services(&deviceServices);
518 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain list of services");
519 uint serviceCount;
520 hr = deviceServices->get_Size(&serviceCount);
521 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain service count");
522
523 if (serviceCount == 0) {
524 emitErrorAndQuitThread("Found devices without services");
525 return;
526 }
527
528 // Windows automatically connects to the device as soon as a service value is read/written.
529 // Thus we read one value in order to establish the connection.
530 for (uint i = 0; i < serviceCount; ++i) {
532 hr = deviceServices->GetAt(i, &service);
533 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain service");
535 hr = service.As(&service3);
536 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not cast service");
538 hr = service3->GetCharacteristicsAsync(&characteristicsOp);
539 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain characteristic");
540 ComPtr<IGattCharacteristicsResult> characteristicsResult;
541 hr = QWinRTFunctions::await(characteristicsOp, characteristicsResult.GetAddressOf(),
542 QWinRTFunctions::ProcessThreadEvents, 5000, earlyExit);
543 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not await characteristic operation");
544 GattCommunicationStatus commStatus;
545 hr = characteristicsResult->get_Status(&commStatus);
546 if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
547 qCWarning(QT_BT_WINDOWS) << "Characteristic operation failed";
548 break;
549 }
551 hr = characteristicsResult->get_Characteristics(&characteristics);
552 if (hr == E_ACCESSDENIED) {
553 // Everything will work as expected up until this point if the
554 // manifest capabilties for bluetooth LE are not set.
555 emitErrorAndQuitThread("Could not obtain characteristic list. "
556 "Please check your manifest capabilities");
557 return;
558 }
559 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain characteristic list");
560 uint characteristicsCount;
561 hr = characteristics->get_Size(&characteristicsCount);
563 "Could not obtain characteristic list's size");
564 for (uint j = 0; j < characteristicsCount; ++j) {
565 ComPtr<IGattCharacteristic> characteristic;
566 hr = characteristics->GetAt(j, &characteristic);
567 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain characteristic");
569 GattCharacteristicProperties props;
570 hr = characteristic->get_CharacteristicProperties(&props);
572 hr, "Could not obtain characteristic's properties");
573 if (!(props & GattCharacteristicProperties_Read))
574 continue;
575 hr = characteristic->ReadValueWithCacheModeAsync(
576 BluetoothCacheMode::BluetoothCacheMode_Uncached, &op);
577 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not read characteristic value");
579 // Reading characteristics can take surprisingly long at the
580 // first time, so we need to have a large the timeout here.
581 hr = QWinRTFunctions::await(op, result.GetAddressOf(),
582 QWinRTFunctions::ProcessThreadEvents, 5000, earlyExit);
583 // E_ILLEGAL_METHOD_CALL will be the result for a device, that is not reachable at
584 // the moment. In this case we should jump back into the outer loop and keep trying.
585 if (hr == E_ILLEGAL_METHOD_CALL)
586 break;
587 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not await characteristic read");
589 hr = result->get_Value(&buffer);
590 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain characteristic value");
591 if (!buffer) {
592 qCDebug(QT_BT_WINDOWS) << "Problem reading value";
593 break;
594 }
595
596 emitConnectedAndQuitThread();
597 return;
598 }
599 }
600 }
601 // If we got here because of mAbortConnection == true, the error message
602 // will not be delivered, so it does not matter. But we need to terminate
603 // the thread anyway!
604 emitErrorAndQuitThread("Connect to device failed due to timeout!");
605}
606
607void QWinRTLowEnergyConnectionHandler::connectToUnpairedDevice()
608{
609 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
611 HRESULT hr = mDevice.As(&device3);
612 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not cast device");
613 ComPtr<IGattDeviceServicesResult> deviceServicesResult;
614 auto earlyExit = [this]() { return mAbortConnection; };
616 while (!mAbortConnection && !deadline.hasExpired()) {
618 hr = device3->GetGattServicesAsync(&deviceServicesOp);
619 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain services");
620 hr = QWinRTFunctions::await(deviceServicesOp, deviceServicesResult.GetAddressOf(),
621 QWinRTFunctions::ProcessMainThreadEvents, 0, earlyExit);
622 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not await services operation");
623
624 GattCommunicationStatus commStatus;
625 hr = deviceServicesResult->get_Status(&commStatus);
626 if (commStatus == GattCommunicationStatus_Unreachable)
627 continue;
628
629 if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
630 emitErrorAndQuitThread("Service operation failed");
631 return;
632 }
633
634 emitConnectedAndQuitThread();
635 return;
636 }
637 // If we got here because of mAbortConnection == true, the error message
638 // will not be delivered, so it does not matter. But we need to terminate
639 // the thread anyway!
640 emitErrorAndQuitThread("Connect to device failed due to timeout!");
641}
642
643void QWinRTLowEnergyConnectionHandler::emitErrorAndQuitThread(const QString &error)
644{
647}
648
649void QWinRTLowEnergyConnectionHandler::emitErrorAndQuitThread(const char *error)
650{
651 emitErrorAndQuitThread(QString::fromUtf8(error));
652}
653
654void QWinRTLowEnergyConnectionHandler::emitConnectedAndQuitThread()
655{
656 emit deviceConnected(mDevice, mGattSession);
658}
659
662{
665 this, &QLowEnergyControllerPrivateWinRT::handleCharacteristicChanged,
667}
668
670{
671 unregisterFromStatusChanges();
672 unregisterFromValueChanges();
673}
674
676{
677}
678
680{
681 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
682 if (remoteDevice.isNull()) {
683 qCWarning(QT_BT_WINDOWS) << "Invalid/null remote device address";
685 return;
686 }
688
690 QThread *thread = new QThread;
691 worker->moveToThread(thread);
698 [this](const QString &msg) { handleConnectionError(msg.toUtf8().constData()); });
701 if (!device || !session) {
702 handleConnectionError("Failed to get device or gatt service");
703 return;
704 }
705 mDevice = device;
706 mGattSession = session;
707
708 if (!registerForStatusChanges() || !registerForMtuChanges()) {
709 handleConnectionError("Failed to register for changes");
710 return;
711 }
712
715 emit q->connected();
716 });
717 thread->start();
718}
719
721{
722 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
726 unregisterFromValueChanges();
727 unregisterFromStatusChanges();
728 unregisterFromMtuChanges();
729 clearAllServices();
730 mGattSession = nullptr;
731 mDevice = nullptr;
733 emit q->disconnected();
734}
735
736HRESULT QLowEnergyControllerPrivateWinRT::getNativeService(const QBluetoothUuid &serviceUuid,
737 NativeServiceCallback callback)
738{
739 if (m_openedServices.contains(serviceUuid)) {
740 callback(m_openedServices.value(serviceUuid));
741 return S_OK;
742 }
743
745 HRESULT hr = mDevice.As(&device3);
746 RETURN_IF_FAILED("Could not convert to IBluetoothDevice3", return hr);
747
749 hr = device3->GetGattServicesForUuidAsync(serviceUuid, &servicesResultOperation);
750 RETURN_IF_FAILED("Could not start async services request", return hr);
751
753 hr = servicesResultOperation->put_Completed(
754 Callback<IAsyncOperationCompletedHandler<
755 GenericAttributeProfile::GattDeviceServicesResult *>>(
756 [thisPtr, callback, &serviceUuid](
757 IAsyncOperation<GattDeviceServicesResult *> *op, AsyncStatus status)
758 {
759 if (thisPtr) {
760 if (status != AsyncStatus::Completed) {
761 qCDebug(QT_BT_WINDOWS) << "Failed to get result of async service request";
762 return S_OK;
763 }
766 HRESULT hr = op->GetResults(&result);
767 RETURN_IF_FAILED("Failed to get result of async service request", return hr);
768
770 hr = result->get_Services(&services);
771 RETURN_IF_FAILED("Failed to extract services from the result", return hr);
772
773 uint servicesCount = 0;
774 hr = services->get_Size(&servicesCount);
775 RETURN_IF_FAILED("Failed to extract services count", return hr);
776
777 if (servicesCount > 0) {
778 if (servicesCount > 1) {
779 qWarning() << "getNativeService: more than one service detected for UUID"
780 << serviceUuid << "The first service will be used.";
781 }
783 hr = services->GetAt(0, &service);
784 if (FAILED(hr)) {
785 qCDebug(QT_BT_WINDOWS) << "Could not obtain native service for Uuid"
786 << serviceUuid;
787 }
788 if (service) {
789 thisPtr->m_openedServices[serviceUuid] = service;
790 callback(service); // Use the service in a custom callback
791 }
792 } else {
793 qCWarning(QT_BT_WINDOWS) << "No services found for Uuid" << serviceUuid;
794 }
795 } else {
796 qCWarning(QT_BT_WINDOWS) << "LE controller was removed while getting native service";
797 }
798 return S_OK;
799 }).Get());
800
801 return hr;
802}
803
804HRESULT QLowEnergyControllerPrivateWinRT::getNativeCharacteristic(
805 const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid,
806 NativeCharacteristicCallback callback)
807{
809 auto serviceCallback = [thisPtr, callback, charUuid](ComPtr<IGattDeviceService> service) {
811 HRESULT hr = service.As(&service3);
812 RETURN_IF_FAILED("Could not cast service to service3", return);
813
815 hr = service3->GetCharacteristicsForUuidAsync(charUuid, &characteristicRequestOp);
816
817 hr = characteristicRequestOp->put_Completed(
818 Callback<IAsyncOperationCompletedHandler<GattCharacteristicsResult *>>(
819 [thisPtr, callback](
820 IAsyncOperation<GattCharacteristicsResult *> *op, AsyncStatus status)
821 {
822 if (thisPtr) {
823 if (status != AsyncStatus::Completed) {
824 qCDebug(QT_BT_WINDOWS) << "Failed to get result of async characteristic "
825 "operation";
826 return S_OK;
827 }
829 HRESULT hr = op->GetResults(&result);
830 RETURN_IF_FAILED("Failed to get result of async characteristic operation",
831 return hr);
832 GattCommunicationStatus status;
833 hr = result->get_Status(&status);
834 if (FAILED(hr) || status != GattCommunicationStatus_Success) {
835 qErrnoWarning(hr, "Native characteristic operation failed.");
836 return S_OK;
837 }
839 hr = result->get_Characteristics(&characteristics);
840 RETURN_IF_FAILED("Could not obtain characteristic list.", return S_OK);
841 uint size;
842 hr = characteristics->get_Size(&size);
843 RETURN_IF_FAILED("Could not obtain characteristic list's size.", return S_OK);
844 if (size != 1)
845 qErrnoWarning("More than 1 characteristic found.");
846 ComPtr<IGattCharacteristic> characteristic;
847 hr = characteristics->GetAt(0, &characteristic);
848 RETURN_IF_FAILED("Could not obtain first characteristic for service", return S_OK);
849 if (characteristic)
850 callback(characteristic); // use the characteristic in a custom callback
851 }
852 return S_OK;
853 }).Get());
854 };
855
856 HRESULT hr = getNativeService(serviceUuid, serviceCallback);
857 if (FAILED(hr))
858 qCDebug(QT_BT_WINDOWS) << "Failed to get native service for" << serviceUuid;
859
860 return hr;
861}
862
863void QLowEnergyControllerPrivateWinRT::registerForValueChanges(const QBluetoothUuid &serviceUuid,
864 const QBluetoothUuid &charUuid)
865{
866 qCDebug(QT_BT_WINDOWS) << "Registering characteristic" << charUuid << "in service"
867 << serviceUuid << "for value changes";
868 for (const ValueChangedEntry &entry : std::as_const(mValueChangedTokens)) {
869 GUID guuid;
870 HRESULT hr;
871 hr = entry.characteristic->get_Uuid(&guuid);
872 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic's Uuid")
873 if (QBluetoothUuid(guuid) == charUuid)
874 return;
875 }
876
877 auto callback = [this, charUuid, serviceUuid](ComPtr<IGattCharacteristic> characteristic) {
878 EventRegistrationToken token;
879 HRESULT hr;
880 hr = characteristic->add_ValueChanged(
881 Callback<ValueChangedHandler>(
882 this, &QLowEnergyControllerPrivateWinRT::onValueChange).Get(),
883 &token);
884 RETURN_IF_FAILED("Could not register characteristic for value changes", return)
885 mValueChangedTokens.append(ValueChangedEntry(characteristic, token));
886 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charUuid << "in service"
887 << serviceUuid << "registered for value changes";
888 };
889
890 HRESULT hr = getNativeCharacteristic(serviceUuid, charUuid, callback);
891 if (FAILED(hr)) {
892 qCDebug(QT_BT_WINDOWS).nospace() << "Could not obtain native characteristic "
893 << charUuid << " from service " << serviceUuid
894 << ". Qt will not be able to signal"
895 << " changes for this characteristic.";
896 }
897}
898
899void QLowEnergyControllerPrivateWinRT::unregisterFromValueChanges()
900{
901 qCDebug(QT_BT_WINDOWS) << "Unregistering " << mValueChangedTokens.size() << " value change tokens";
902 HRESULT hr;
903 for (const ValueChangedEntry &entry : std::as_const(mValueChangedTokens)) {
904 if (!entry.characteristic) {
905 qCWarning(QT_BT_WINDOWS) << "Unregistering from value changes for characteristic failed."
906 << "Characteristic has been deleted";
907 continue;
908 }
909 hr = entry.characteristic->remove_ValueChanged(entry.token);
910 if (FAILED(hr))
911 qCWarning(QT_BT_WINDOWS) << "Unregistering from value changes for characteristic failed.";
912 }
913 mValueChangedTokens.clear();
914}
915
916HRESULT QLowEnergyControllerPrivateWinRT::onValueChange(IGattCharacteristic *characteristic, IGattValueChangedEventArgs *args)
917{
918 HRESULT hr;
920 hr = characteristic->get_AttributeHandle(&handle);
921 RETURN_IF_FAILED("Could not obtain characteristic's handle", return S_OK)
923 hr = args->get_CharacteristicValue(&buffer);
924 RETURN_IF_FAILED("Could not obtain characteristic's value", return S_OK)
926 return S_OK;
927}
928HRESULT QLowEnergyControllerPrivateWinRT::onMtuChange(IGattSession *session, IInspectable *args)
929{
930 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
931 if (session != mGattSession.Get()) {
932 qCWarning(QT_BT_WINDOWS) << "Got MTU changed event for wrong or outdated GattSession.";
933 return S_OK;
934 }
935
937 emit q->mtuChanged(mtu());
938 return S_OK;
939}
940
941bool QLowEnergyControllerPrivateWinRT::registerForStatusChanges()
942{
943 if (!mDevice)
944 return false;
945
946 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
947
948 HRESULT hr;
949 hr = mDevice->add_ConnectionStatusChanged(
950 Callback<StatusHandler>(this, &QLowEnergyControllerPrivateWinRT::onStatusChange).Get(),
951 &mStatusChangedToken);
952 RETURN_IF_FAILED("Could not add status callback", return false)
953 return true;
954}
955
956void QLowEnergyControllerPrivateWinRT::unregisterFromStatusChanges()
957{
958 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
959 if (mDevice && mStatusChangedToken.value) {
960 mDevice->remove_ConnectionStatusChanged(mStatusChangedToken);
961 mStatusChangedToken.value = 0;
962 }
963}
964
965bool QLowEnergyControllerPrivateWinRT::registerForMtuChanges()
966{
967 if (!mDevice || !mGattSession)
968 return false;
969
970 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
971
972 HRESULT hr;
973 hr = mGattSession->add_MaxPduSizeChanged(
974 Callback<MtuHandler>(this, &QLowEnergyControllerPrivateWinRT::onMtuChange).Get(),
975 &mMtuChangedToken);
976 RETURN_IF_FAILED("Could not add MTU callback", return false)
977 return true;
978}
979
980void QLowEnergyControllerPrivateWinRT::unregisterFromMtuChanges()
981{
982 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
983 if (mDevice && mGattSession && mMtuChangedToken.value) {
984 mGattSession->remove_MaxPduSizeChanged(mMtuChangedToken);
985 mMtuChangedToken.value = 0;
986 }
987}
988
989HRESULT QLowEnergyControllerPrivateWinRT::onStatusChange(IBluetoothLEDevice *dev, IInspectable *)
990{
992 BluetoothConnectionStatus status;
993 HRESULT hr;
994 hr = dev->get_ConnectionStatus(&status);
995 RETURN_IF_FAILED("Could not obtain connection status", return S_OK)
997 && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) {
999 emit q->connected();
1001 && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) {
1003 unregisterFromValueChanges();
1004 unregisterFromStatusChanges();
1005 unregisterFromMtuChanges();
1006 mGattSession = nullptr;
1007 mDevice = nullptr;
1010 emit q->disconnected();
1011 }
1012 return S_OK;
1013}
1014
1015void QLowEnergyControllerPrivateWinRT::obtainIncludedServices(
1018{
1021 HRESULT hr = service.As(&service3);
1022 RETURN_IF_FAILED("Could not cast service", return);
1024 hr = service3->GetIncludedServicesAsync(&op);
1025 // Some devices return ERROR_ACCESS_DISABLED_BY_POLICY
1026 RETURN_IF_FAILED("Could not obtain included services", return);
1028 hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000);
1029 RETURN_IF_FAILED("Could not await service operation", return);
1030 // The device can be disconnected by the time we return from await()
1032 return;
1033 GattCommunicationStatus status;
1034 hr = result->get_Status(&status);
1035 if (FAILED(hr) || status != GattCommunicationStatus_Success) {
1036 qErrnoWarning("Could not obtain list of included services");
1037 return;
1038 }
1040 hr = result->get_Services(&includedServices);
1041 RETURN_IF_FAILED("Could not obtain service list", return);
1042
1043 uint count;
1044 hr = includedServices->get_Size(&count);
1045 RETURN_IF_FAILED("Could not obtain service list's size", return);
1046 for (uint i = 0; i < count; ++i) {
1047 ComPtr<IGattDeviceService> includedService;
1048 hr = includedServices->GetAt(i, &includedService);
1049 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service from list");
1050 GUID guuid;
1051 hr = includedService->get_Uuid(&guuid);
1052 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain included service's Uuid");
1053 const QBluetoothUuid includedUuid(guuid);
1055 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__
1056 << "Changing service pointer from thread"
1058 if (serviceList.contains(includedUuid)) {
1059 includedPointer = serviceList.value(includedUuid);
1060 } else {
1062 priv->uuid = includedUuid;
1063 priv->setController(this);
1064
1066 serviceList.insert(includedUuid, includedPointer);
1067 }
1068 includedPointer->type |= QLowEnergyService::IncludedService;
1069 servicePointer->includedServices.append(includedUuid);
1070
1071 obtainIncludedServices(includedPointer, includedService);
1072
1073 emit q->serviceDiscovered(includedUuid);
1074 }
1075}
1076
1077HRESULT QLowEnergyControllerPrivateWinRT::onServiceDiscoveryFinished(ABI::Windows::Foundation::IAsyncOperation<GattDeviceServicesResult *> *op, AsyncStatus status)
1078{
1079 // Check if the device is in the proper state, because it can already be
1080 // disconnected when the callback arrives.
1081 // Also the callback can theoretically come when the connection is
1082 // reestablisheed again (for example, if the user quickly clicks
1083 // "Disconnect" and then "Connect" again in some UI). But we can probably
1084 // omit such details, as we are connecting to the same device anyway.
1086 return S_OK;
1087
1089 if (status != AsyncStatus::Completed) {
1090 qCDebug(QT_BT_WINDOWS) << "Could not obtain services";
1091 return S_OK;
1092 }
1095 HRESULT hr = op->GetResults(&result);
1096 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service discovery result",
1097 return S_OK);
1098 GattCommunicationStatus commStatus;
1099 hr = result->get_Status(&commStatus);
1100 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service discovery status",
1101 return S_OK);
1102 if (commStatus != GattCommunicationStatus_Success)
1103 return S_OK;
1104
1105 hr = result->get_Services(&deviceServices);
1106 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service list",
1107 return S_OK);
1108
1109 uint serviceCount;
1110 hr = deviceServices->get_Size(&serviceCount);
1111 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service list size",
1112 return S_OK);
1113 for (uint i = 0; i < serviceCount; ++i) {
1114 ComPtr<IGattDeviceService> deviceService;
1115 hr = deviceServices->GetAt(i, &deviceService);
1116 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service");
1117 GUID guuid;
1118 hr = deviceService->get_Uuid(&guuid);
1119 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service's Uuid");
1120 const QBluetoothUuid service(guuid);
1121 m_openedServices[service] = deviceService;
1122
1123 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__
1124 << "Changing service pointer from thread"
1127 if (serviceList.contains(service)) {
1128 pointer = serviceList.value(service);
1129 } else {
1131 priv->uuid = service;
1132 priv->setController(this);
1133
1135 serviceList.insert(service, pointer);
1136 }
1138
1139 obtainIncludedServices(pointer, deviceService);
1140 // The obtainIncludedServices method calls await(), so the device can be
1141 // disconnected by the time we return from it. TODO - rewrite in an
1142 // async way!
1144 emit q->discoveryFinished(); // Probably not needed when the device
1145 // is already disconnected?
1146 return S_OK;
1147 }
1148
1149 emit q->serviceDiscovered(service);
1150 }
1151
1153 emit q->discoveryFinished();
1154
1155 return S_OK;
1156}
1157
1158void QLowEnergyControllerPrivateWinRT::clearAllServices()
1159{
1160 // These services will be closed in the respective
1161 // QWinRTLowEnergyServiceHandler workers (in background threads).
1162 for (auto &uuid : m_requestDetailsServiceUuids)
1163 m_openedServices.remove(uuid);
1164 m_requestDetailsServiceUuids.clear();
1165
1166 for (auto service : m_openedServices) {
1167 closeDeviceService(service);
1168 }
1169 m_openedServices.clear();
1170}
1171
1172void QLowEnergyControllerPrivateWinRT::closeAndRemoveService(const QBluetoothUuid &uuid)
1173{
1174 auto service = m_openedServices.take(uuid);
1175 if (service)
1176 closeDeviceService(service);
1177}
1178
1180{
1181 qCDebug(QT_BT_WINDOWS) << "Service discovery initiated";
1182 // clear the previous services cache, as we request the services again
1183 clearAllServices();
1185 HRESULT hr = mDevice.As(&device3);
1186 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast device", return);
1188 hr = device3->GetGattServicesAsync(&asyncResult);
1189 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain services", return);
1190 hr = asyncResult->put_Completed(
1191 Callback<IAsyncOperationCompletedHandler<GenericAttributeProfile::GattDeviceServicesResult *>>(
1192 this, &QLowEnergyControllerPrivateWinRT::onServiceDiscoveryFinished).Get());
1193 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not register services discovery callback",
1194 return);
1195}
1196
1199{
1200 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << service;
1201 if (!serviceList.contains(service)) {
1202 qCWarning(QT_BT_WINDOWS) << "Discovery done of unknown service:"
1203 << service.toString();
1204 return;
1205 }
1206
1207 // clear the cache to rediscover service details
1208 closeAndRemoveService(service);
1209
1210 auto serviceCallback = [service, mode, this](ComPtr<IGattDeviceService> deviceService) {
1211 discoverServiceDetailsHelper(service, mode, deviceService);
1212 };
1213
1214 HRESULT hr = getNativeService(service, serviceCallback);
1215 if (FAILED(hr))
1216 qCDebug(QT_BT_WINDOWS) << "Could not obtain native service for uuid " << service;
1217}
1218
1219void QLowEnergyControllerPrivateWinRT::discoverServiceDetailsHelper(
1221 GattDeviceServiceComPtr deviceService)
1222{
1223 auto reactOnDiscoveryError = [](QSharedPointer<QLowEnergyServicePrivate> service,
1224 const QString &msg)
1225 {
1226 qCDebug(QT_BT_WINDOWS) << msg;
1227 service->setError(QLowEnergyService::UnknownError);
1228 service->setState(QLowEnergyService::RemoteService);
1229 };
1230 //update service data
1232 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1235 ComPtr<IGattDeviceService3> deviceService3;
1236 HRESULT hr = deviceService.As(&deviceService3);
1237 if (FAILED(hr)) {
1238 reactOnDiscoveryError(pointer, QStringLiteral("Could not cast service: %1").arg(hr));
1239 return;
1240 }
1242 hr = deviceService3->GetIncludedServicesAsync(&op);
1243 if (FAILED(hr)) {
1244 reactOnDiscoveryError(pointer,
1245 QStringLiteral("Could not obtain included service list: %1").arg(hr));
1246 return;
1247 }
1249 hr = QWinRTFunctions::await(op, result.GetAddressOf());
1250 if (FAILED(hr)) {
1251 reactOnDiscoveryError(pointer,
1252 QStringLiteral("Could not await service operation: %1").arg(hr));
1253 return;
1254 }
1255 GattCommunicationStatus status;
1256 hr = result->get_Status(&status);
1257 if (FAILED(hr) || status != GattCommunicationStatus_Success) {
1258 reactOnDiscoveryError(pointer,
1259 QStringLiteral("Obtaining list of included services failed: %1").
1260 arg(hr));
1261 return;
1262 }
1264 hr = result->get_Services(&deviceServices);
1265 if (FAILED(hr)) {
1266 reactOnDiscoveryError(pointer,
1267 QStringLiteral("Could not obtain service list from result: %1").
1268 arg(hr));
1269 return;
1270 }
1271 uint serviceCount;
1272 hr = deviceServices->get_Size(&serviceCount);
1273 if (FAILED(hr)) {
1274 reactOnDiscoveryError(pointer,
1275 QStringLiteral("Could not obtain included service list's size: %1").
1276 arg(hr));
1277 return;
1278 }
1279 for (uint i = 0; i < serviceCount; ++i) {
1280 ComPtr<IGattDeviceService> includedService;
1281 hr = deviceServices->GetAt(i, &includedService);
1282 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service from list")
1283 GUID guuid;
1284 hr = includedService->get_Uuid(&guuid);
1285 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service Uuid")
1286
1287 const QBluetoothUuid service(guuid);
1288 if (service.isNull()) {
1289 qCDebug(QT_BT_WINDOWS) << "Could not find service";
1290 continue;
1291 }
1292
1293 pointer->includedServices.append(service);
1294
1295 // update the type of the included service
1296 QSharedPointer<QLowEnergyServicePrivate> otherService = serviceList.value(service);
1297 if (!otherService.isNull())
1298 otherService->type |= QLowEnergyService::IncludedService;
1299 }
1300
1302 new QWinRTLowEnergyServiceHandler(service, deviceService3, mode);
1303 m_requestDetailsServiceUuids.insert(service);
1304 QThread *thread = new QThread;
1305 worker->moveToThread(thread);
1312 this, &QLowEnergyControllerPrivateWinRT::handleServiceHandlerError);
1314 [this](const QBluetoothUuid &service, QHash<QLowEnergyHandle,
1316 QLowEnergyHandle startHandle, QLowEnergyHandle endHandle) {
1317 if (!serviceList.contains(service)) {
1318 qCWarning(QT_BT_WINDOWS)
1319 << "Discovery complete for unknown service:" << service.toString();
1320 return;
1321 }
1322 m_requestDetailsServiceUuids.remove(service);
1323
1325 pointer->startHandle = startHandle;
1326 pointer->endHandle = endHandle;
1327 pointer->characteristicList = charList;
1328
1329 for (const QBluetoothUuid &indicateChar : std::as_const(indicateChars))
1330 registerForValueChanges(service, indicateChar);
1331
1333 });
1334 thread->start();
1335}
1336
1341{
1344}
1345
1347{
1349}
1350
1352{
1354}
1355
1358 const QLowEnergyHandle charHandle)
1359{
1360 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << service << charHandle;
1361 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1363 Q_ASSERT(!service.isNull());
1367 return;
1368 }
1369
1370 if (!service->characteristicList.contains(charHandle)) {
1371 qCDebug(QT_BT_WINDOWS) << charHandle << "could not be found in service" << service->uuid;
1373 return;
1374 }
1375
1376 const auto charData = service->characteristicList.value(charHandle);
1377 if (!(charData.properties & QLowEnergyCharacteristic::Read))
1378 qCDebug(QT_BT_WINDOWS) << "Read flag is not set for characteristic" << charData.uuid;
1379
1380 auto characteristicCallback = [charHandle, service, this](
1381 ComPtr<IGattCharacteristic> characteristic) {
1382 readCharacteristicHelper(service, charHandle, characteristic);
1383 };
1384
1385 HRESULT hr = getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback);
1386 if (FAILED(hr)) {
1387 qCDebug(QT_BT_WINDOWS) << "Could not obtain native characteristic" << charData.uuid
1388 << "from service" << service->uuid;
1390 }
1391}
1392
1393void QLowEnergyControllerPrivateWinRT::readCharacteristicHelper(
1395 const QLowEnergyHandle charHandle,
1396 GattCharacteristicComPtr characteristic)
1397{
1399 HRESULT hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp);
1400 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not read characteristic",
1402 auto readCompletedLambda = [charHandle, service]
1403 (IAsyncOperation<GattReadResult*> *op, AsyncStatus status)
1404 {
1405 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
1406 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charHandle << "read operation failed.";
1408 return S_OK;
1409 }
1410 ComPtr<IGattReadResult> characteristicValue;
1411 HRESULT hr;
1412 hr = op->GetResults(&characteristicValue);
1413 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for characteristic", service,
1415
1416 const QByteArray value = byteArrayFromGattResult(characteristicValue);
1417 auto charData = service->characteristicList.value(charHandle);
1418 charData.value = value;
1419 service->characteristicList.insert(charHandle, charData);
1420 emit service->characteristicRead(QLowEnergyCharacteristic(service, charHandle), value);
1421 return S_OK;
1422 };
1423 hr = readOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattReadResult *>>(
1424 readCompletedLambda).Get());
1425 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register characteristic read callback",
1427}
1428
1431 const QLowEnergyHandle charHandle,
1432 const QLowEnergyHandle descHandle)
1433{
1434 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << service << charHandle << descHandle;
1435 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1437 Q_ASSERT(!service.isNull());
1439 service->setError(QLowEnergyService::DescriptorReadError);
1441 return;
1442 }
1443
1444 if (!service->characteristicList.contains(charHandle)) {
1445 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "in characteristic" << charHandle
1446 << "cannot be found in service" << service->uuid;
1447 service->setError(QLowEnergyService::DescriptorReadError);
1448 return;
1449 }
1450
1451 const auto charData = service->characteristicList.value(charHandle);
1452
1453 auto characteristicCallback = [charHandle, descHandle, service, this](
1454 ComPtr<IGattCharacteristic> characteristic) {
1455 readDescriptorHelper(service, charHandle, descHandle, characteristic);
1456 };
1457
1458 HRESULT hr = getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback);
1459 if (FAILED(hr)) {
1460 qCDebug(QT_BT_WINDOWS) << "Could not obtain native characteristic" << charData.uuid
1461 << "from service" << service->uuid;
1462 service->setError(QLowEnergyService::DescriptorReadError);
1463 }
1464}
1465
1466void QLowEnergyControllerPrivateWinRT::readDescriptorHelper(
1468 const QLowEnergyHandle charHandle,
1469 const QLowEnergyHandle descHandle,
1470 GattCharacteristicComPtr characteristic)
1471{
1472 // Get native descriptor
1473 const auto charData = service->characteristicList.value(charHandle);
1474 if (!charData.descriptorList.contains(descHandle)) {
1475 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "cannot be found in characteristic"
1476 << charHandle;
1477 }
1478 const auto descData = charData.descriptorList.value(descHandle);
1479 const QBluetoothUuid descUuid = descData.uuid;
1480 if (descUuid ==
1483 HRESULT hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp);
1484 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not read client characteristic configuration",
1486 auto readCompletedLambda = [charHandle, descHandle, service]
1488 {
1489 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
1490 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "read operation failed";
1492 return S_OK;
1493 }
1495 HRESULT hr;
1496 hr = op->GetResults(&iValue);
1497 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for descriptor", service,
1499 GattClientCharacteristicConfigurationDescriptorValue value;
1500 hr = iValue->get_ClientCharacteristicConfigurationDescriptor(&value);
1501 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain value for descriptor", service,
1503 quint16 result = 0;
1504 bool correct = false;
1505 if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) {
1507 correct = true;
1508 }
1509 if (value & GattClientCharacteristicConfigurationDescriptorValue_Notify) {
1511 correct = true;
1512 }
1513 if (value == GattClientCharacteristicConfigurationDescriptorValue_None)
1514 correct = true;
1515 if (!correct) {
1516 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle
1517 << "read operation failed. Obtained unexpected value.";
1519 return S_OK;
1520 }
1523 descData.value = QByteArray(2, Qt::Uninitialized);
1524 qToLittleEndian(result, descData.value.data());
1525 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
1526 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
1527 descData.value);
1528 return S_OK;
1529 };
1530 hr = readOp->put_Completed(
1531 Callback<IAsyncOperationCompletedHandler<ClientCharConfigDescriptorResult *>>(
1532 readCompletedLambda).Get());
1533 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor read callback",
1535 return;
1536 }
1537
1538 ComPtr<IGattCharacteristic3> characteristic3;
1539 HRESULT hr = characteristic.As(&characteristic3);
1540 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast characteristic",
1543 hr = characteristic3->GetDescriptorsForUuidAsync(descData.uuid, &op);
1544 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor for uuid",
1547 hr = QWinRTFunctions::await(op, result.GetAddressOf(),
1548 QWinRTFunctions::ProcessMainThreadEvents, 5000);
1549 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await descritpor read result",
1551
1552 GattCommunicationStatus commStatus;
1553 hr = result->get_Status(&commStatus);
1554 if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
1555 qErrnoWarning("Could not obtain list of descriptors");
1557 return;
1558 }
1559
1561 hr = result->get_Descriptors(&descriptors);
1562 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor list",
1564 uint size;
1565 hr = descriptors->get_Size(&size);
1566 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await descritpor list's size",
1568 if (size == 0) {
1569 qCWarning(QT_BT_WINDOWS) << "No descriptor with uuid" << descData.uuid << "was found.";
1571 return;
1572 } else if (size > 1) {
1573 qCWarning(QT_BT_WINDOWS) << "There is more than 1 descriptor with uuid" << descData.uuid;
1574 }
1575
1576 ComPtr<IGattDescriptor> descriptor;
1577 hr = descriptors->GetAt(0, &descriptor);
1578 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descritpor from list",
1581 hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp);
1582 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not read descriptor value",
1584 auto readCompletedLambda = [charHandle, descHandle, descUuid, service]
1585 (IAsyncOperation<GattReadResult*> *op, AsyncStatus status)
1586 {
1587 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
1588 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "read operation failed";
1590 return S_OK;
1591 }
1592 ComPtr<IGattReadResult> descriptorValue;
1593 HRESULT hr;
1594 hr = op->GetResults(&descriptorValue);
1595 if (FAILED(hr)) {
1596 qCDebug(QT_BT_WINDOWS) << "Could not obtain result for descriptor" << descHandle;
1598 return S_OK;
1599 }
1601 descData.uuid = descUuid;
1603 descData.value = byteArrayFromGattResult(descriptorValue, true);
1604 else
1605 descData.value = byteArrayFromGattResult(descriptorValue);
1606 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
1607 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
1608 descData.value);
1609 return S_OK;
1610 };
1611 hr = readOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattReadResult *>>(
1612 readCompletedLambda).Get());
1613 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor read callback",
1615}
1616
1619 const QLowEnergyHandle charHandle,
1620 const QByteArray &newValue,
1622{
1623 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << service << charHandle << newValue << mode;
1624 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1626 Q_ASSERT(!service.isNull());
1630 return;
1631 }
1632 if (!service->characteristicList.contains(charHandle)) {
1633 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charHandle << "cannot be found in service"
1634 << service->uuid;
1636 return;
1637 }
1638
1639 QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
1640 const bool writeWithResponse = mode == QLowEnergyService::WriteWithResponse;
1641 if (!(charData.properties & (writeWithResponse ? QLowEnergyCharacteristic::Write
1643 qCDebug(QT_BT_WINDOWS) << "Write flag is not set for characteristic" << charHandle;
1644
1645 auto characteristicCallback = [charHandle, service, newValue, writeWithResponse, this](
1646 ComPtr<IGattCharacteristic> characteristic) {
1647 writeCharacteristicHelper(service, charHandle, newValue, writeWithResponse,
1648 characteristic);
1649 };
1650
1651 HRESULT hr = getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback);
1652 if (FAILED(hr)) {
1653 qCDebug(QT_BT_WINDOWS) << "Could not obtain native characteristic" << charData.uuid
1654 << "from service" << service->uuid;
1656 }
1657}
1658
1659void QLowEnergyControllerPrivateWinRT::writeCharacteristicHelper(
1661 const QLowEnergyHandle charHandle, const QByteArray &newValue,
1662 bool writeWithResponse, GattCharacteristicComPtr characteristic)
1663{
1665 HRESULT hr = GetActivationFactory(
1666 HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
1667 &bufferFactory);
1668 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain buffer factory",
1671 const quint32 length = quint32(newValue.length());
1672 hr = bufferFactory->Create(length, &buffer);
1673 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not create buffer",
1675 hr = buffer->put_Length(length);
1676 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer length",
1679 hr = buffer.As(&byteAccess);
1680 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast buffer",
1682 byte *bytes;
1683 hr = byteAccess->Buffer(&bytes);
1684 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer",
1686 memcpy(bytes, newValue, length);
1688 GattWriteOption option = writeWithResponse ? GattWriteOption_WriteWithResponse
1689 : GattWriteOption_WriteWithoutResponse;
1690 hr = characteristic->WriteValueWithOptionAsync(buffer.Get(), option, &writeOp);
1691 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could write characteristic",
1693 const auto charData = service->characteristicList.value(charHandle);
1695 auto writeCompletedLambda =
1696 [charData, charHandle, newValue, service, writeWithResponse, thisPtr]
1697 (IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status)
1698 {
1699 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
1700 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charHandle << "write operation failed";
1702 return S_OK;
1703 }
1704 GattCommunicationStatus result;
1705 HRESULT hr;
1706 hr = op->GetResults(&result);
1707 if (hr == E_BLUETOOTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH) {
1708 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charHandle
1709 << "write operation was tried with invalid value length";
1711 return S_OK;
1712 }
1713 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain characteristic write result", service,
1715 if (result != GattCommunicationStatus_Success) {
1716 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charHandle << "write operation failed";
1718 return S_OK;
1719 }
1720 // only update cache when property is readable. Otherwise it remains
1721 // empty.
1722 if (thisPtr && charData.properties & QLowEnergyCharacteristic::Read)
1723 thisPtr->updateValueOfCharacteristic(charHandle, newValue, false);
1724 if (writeWithResponse) {
1725 emit service->characteristicWritten(QLowEnergyCharacteristic(service, charHandle),
1726 newValue);
1727 }
1728 return S_OK;
1729 };
1730 hr = writeOp->put_Completed(
1731 Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus>>(
1732 writeCompletedLambda).Get());
1733 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register characteristic write callback",
1735}
1736
1739 const QLowEnergyHandle charHandle,
1740 const QLowEnergyHandle descHandle,
1741 const QByteArray &newValue)
1742{
1743 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << service << charHandle << descHandle << newValue;
1744 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1746 Q_ASSERT(!service.isNull());
1748 service->setError(QLowEnergyService::DescriptorWriteError);
1750 return;
1751 }
1752
1753 if (!service->characteristicList.contains(charHandle)) {
1754 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "in characteristic" << charHandle
1755 << "could not be found in service" << service->uuid;
1756 service->setError(QLowEnergyService::DescriptorWriteError);
1757 return;
1758 }
1759
1760 const auto charData = service->characteristicList.value(charHandle);
1761
1762 auto characteristicCallback = [descHandle, charHandle, service, newValue, this](
1763 ComPtr<IGattCharacteristic> characteristic) {
1764 writeDescriptorHelper(service, charHandle, descHandle, newValue, characteristic);
1765 };
1766
1767 HRESULT hr = getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback);
1768 if (FAILED(hr)) {
1769 qCDebug(QT_BT_WINDOWS) << "Could not obtain native characteristic" << charData.uuid
1770 << "from service" << service->uuid;
1771 service->setError(QLowEnergyService::DescriptorWriteError);
1772 }
1773}
1774
1775void QLowEnergyControllerPrivateWinRT::writeDescriptorHelper(
1777 const QLowEnergyHandle charHandle,
1778 const QLowEnergyHandle descHandle,
1779 const QByteArray &newValue,
1780 GattCharacteristicComPtr characteristic)
1781{
1782 // Get native descriptor
1783 const auto charData = service->characteristicList.value(charHandle);
1784 if (!charData.descriptorList.contains(descHandle)) {
1785 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle
1786 << "could not be found in Characteristic" << charHandle;
1787 }
1788
1789 QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle);
1790 if (descData.uuid ==
1792 GattClientCharacteristicConfigurationDescriptorValue value;
1793 quint16 intValue = qFromLittleEndian<quint16>(newValue);
1794 if (intValue & GattClientCharacteristicConfigurationDescriptorValue_Indicate
1795 && intValue & GattClientCharacteristicConfigurationDescriptorValue_Notify) {
1796 qCWarning(QT_BT_WINDOWS) << "Setting both Indicate and Notify "
1797 "is not supported on WinRT";
1798 value = GattClientCharacteristicConfigurationDescriptorValue(
1799 (GattClientCharacteristicConfigurationDescriptorValue_Indicate
1800 | GattClientCharacteristicConfigurationDescriptorValue_Notify));
1801 } else if (intValue & GattClientCharacteristicConfigurationDescriptorValue_Indicate) {
1802 value = GattClientCharacteristicConfigurationDescriptorValue_Indicate;
1803 } else if (intValue & GattClientCharacteristicConfigurationDescriptorValue_Notify) {
1804 value = GattClientCharacteristicConfigurationDescriptorValue_Notify;
1805 } else if (intValue == 0) {
1806 value = GattClientCharacteristicConfigurationDescriptorValue_None;
1807 } else {
1808 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle
1809 << "write operation failed: Invalid value";
1811 return;
1812 }
1814 HRESULT hr =
1815 characteristic->WriteClientCharacteristicConfigurationDescriptorAsync(value, &writeOp);
1816 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not write client characteristic configuration",
1819 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
1820 (IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status)
1821 {
1822 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
1823 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "write operation failed";
1825 return S_OK;
1826 }
1827 GattCommunicationStatus result;
1828 HRESULT hr;
1829 hr = op->GetResults(&result);
1830 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for descriptor", service,
1832 if (result != GattCommunicationStatus_Success) {
1833 qCWarning(QT_BT_WINDOWS) << "Descriptor" << descHandle << "write operation failed";
1835 return S_OK;
1836 }
1837 if (thisPtr)
1838 thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue, false);
1839 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
1840 newValue);
1841 return S_OK;
1842 };
1843 hr = writeOp->put_Completed(
1844 Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus >>(
1845 writeCompletedLambda).Get());
1846 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor write callback",
1848 return;
1849 }
1850
1851 ComPtr<IGattCharacteristic3> characteristic3;
1852 HRESULT hr = characteristic.As(&characteristic3);
1853 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast characteristic",
1856 hr = characteristic3->GetDescriptorsForUuidAsync(descData.uuid, &op);
1857 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor from Uuid",
1860 hr = QWinRTFunctions::await(op, result.GetAddressOf(),
1861 QWinRTFunctions::ProcessMainThreadEvents, 5000);
1862 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await descriptor operation",
1864 GattCommunicationStatus commStatus;
1865 hr = result->get_Status(&commStatus);
1866 if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
1867 qCWarning(QT_BT_WINDOWS) << "Descriptor operation failed";
1869 return;
1870 }
1872 hr = result->get_Descriptors(&descriptors);
1873 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain list of descriptors",
1875 uint size;
1876 hr = descriptors->get_Size(&size);
1877 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain list of descriptors' size",
1879 if (size == 0) {
1880 qCWarning(QT_BT_WINDOWS) << "No descriptor with uuid" << descData.uuid << "was found.";
1881 return;
1882 } else if (size > 1) {
1883 qCWarning(QT_BT_WINDOWS) << "There is more than 1 descriptor with uuid" << descData.uuid;
1884 }
1885 ComPtr<IGattDescriptor> descriptor;
1886 hr = descriptors->GetAt(0, &descriptor);
1887 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor",
1890 hr = GetActivationFactory(
1891 HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
1892 &bufferFactory);
1893 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain buffer factory",
1896 const quint32 length = quint32(newValue.length());
1897 hr = bufferFactory->Create(length, &buffer);
1898 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not create buffer",
1900 hr = buffer->put_Length(length);
1901 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer length",
1904 hr = buffer.As(&byteAccess);
1905 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast buffer",
1907 byte *bytes;
1908 hr = byteAccess->Buffer(&bytes);
1909 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer",
1911 memcpy(bytes, newValue, length);
1913 hr = descriptor->WriteValueAsync(buffer.Get(), &writeOp);
1914 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not write descriptor value",
1917 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
1918 (IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status)
1919 {
1920 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
1921 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "write operation failed";
1923 return S_OK;
1924 }
1925 GattCommunicationStatus result;
1926 HRESULT hr;
1927 hr = op->GetResults(&result);
1928 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for descriptor", service,
1930 if (result != GattCommunicationStatus_Success) {
1931 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "write operation failed";
1933 return S_OK;
1934 }
1935 if (thisPtr)
1936 thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue, false);
1937 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
1938 newValue);
1939 return S_OK;
1940 };
1941 hr = writeOp->put_Completed(
1942 Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus>>(
1943 writeCompletedLambda).Get());
1944 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor write callback",
1946}
1947
1950{
1952}
1953
1955{
1956 uint16_t mtu = 23;
1957 if (!mGattSession) {
1958 qCDebug(QT_BT_WINDOWS) << "mtu queried before GattSession available. Using default mtu.";
1959 return mtu;
1960 }
1961
1962 HRESULT hr = mGattSession->get_MaxPduSize(&mtu);
1963 RETURN_IF_FAILED("could not obtain MTU size", return mtu);
1964 qCDebug(QT_BT_WINDOWS) << "mtu determined to be" << mtu;
1965 return mtu;
1966}
1967
1968void QLowEnergyControllerPrivateWinRT::handleCharacteristicChanged(
1969 quint16 charHandle, const QByteArray &data)
1970{
1971 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << charHandle << data;
1972 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1975 serviceForHandle(charHandle);
1976 if (service.isNull())
1977 return;
1978
1979 qCDebug(QT_BT_WINDOWS) << "Characteristic change notification" << service->uuid
1980 << charHandle << data.toHex();
1981
1982 QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle);
1983 if (!characteristic.isValid()) {
1984 qCWarning(QT_BT_WINDOWS) << "characteristicChanged: Cannot find characteristic";
1985 return;
1986 }
1987
1988 // only update cache when property is readable. Otherwise it remains
1989 // empty.
1990 if (characteristic.properties() & QLowEnergyCharacteristic::Read)
1991 updateValueOfCharacteristic(characteristic.attributeHandle(),
1992 data, false);
1993 emit service->characteristicChanged(characteristic, data);
1994}
1995
1996void QLowEnergyControllerPrivateWinRT::handleServiceHandlerError(const QString &error)
1997{
1999 return;
2000
2001 qCWarning(QT_BT_WINDOWS) << "Error while discovering services:" << error;
2004}
2005
2006void QLowEnergyControllerPrivateWinRT::handleConnectionError(const char *logMessage)
2007{
2008 qCWarning(QT_BT_WINDOWS) << logMessage;
2011 unregisterFromStatusChanges();
2012 unregisterFromMtuChanges();
2013}
2014
2016
2017#include "qlowenergycontroller_winrt.moc"
IOBluetoothDevice * device
std::vector< ObjCStrongReference< CBMutableService > > services
\inmodule QtBluetooth
\inmodule QtBluetooth
Pairing
This enum describes the pairing state between the two Bluetooth devices.
Pairing pairingStatus(const QBluetoothAddress &address) const
Returns the current bluetooth pairing status of address, if it's unpaired, paired,...
\inmodule QtBluetooth
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
qsizetype length() const noexcept
Same as size().
Definition qbytearray.h:479
\inmodule QtCore
bool hasExpired() const noexcept
Returns true if this QDeadlineTimer object has expired, false if there remains time left.
\inmodule QtCore
Definition qhash.h:818
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
void append(parameter_type t)
Definition qlist.h:441
void clear()
Definition qlist.h:417
The QLowEnergyAdvertisingData class represents the data to be broadcast during Bluetooth Low Energy a...
The QLowEnergyAdvertisingParameters class represents the parameters used for Bluetooth Low Energy adv...
QLowEnergyCharacteristic::PropertyTypes properties() const
Returns the properties of the characteristic.
bool isValid() const
Returns true if the QLowEnergyCharacteristic object is valid, otherwise returns false.
The QLowEnergyConnectionParameters class is used when requesting or reporting an update of the parame...
void discoverServiceDetails(const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode) override
void characteristicChanged(quint16 charHandle, const QByteArray &data)
void addToGenericAttributeList(const QLowEnergyServiceData &service, QLowEnergyHandle startHandle) override
void readDescriptor(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle) override
void writeDescriptor(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle, const QByteArray &newValue) override
void startAdvertising(const QLowEnergyAdvertisingParameters &params, const QLowEnergyAdvertisingData &advertisingData, const QLowEnergyAdvertisingData &scanResponseData) override
void readCharacteristic(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle) override
void writeCharacteristic(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QByteArray &newValue, QLowEnergyService::WriteMode mode) override
void requestConnectionUpdate(const QLowEnergyConnectionParameters &params) override
QLowEnergyCharacteristic characteristicForHandle(QLowEnergyHandle handle)
Returns a valid characteristic if the given handle is the handle of the characteristic itself or one ...
QSharedPointer< QLowEnergyServicePrivate > serviceForHandle(QLowEnergyHandle handle)
QLowEnergyController::Error error
void setError(QLowEnergyController::Error newError)
quint16 updateValueOfCharacteristic(QLowEnergyHandle charHandle, const QByteArray &value, bool appendValue)
Returns the length of the updated characteristic value.
QLowEnergyController::ControllerState state
void setState(QLowEnergyController::ControllerState newState)
\inmodule QtBluetooth
\inmodule QtBluetooth
The QLowEnergyServiceData class is used to set up GATT service data. \inmodule QtBluetooth.
DiscoveryMode
This enum lists service discovery modes.
WriteMode
This enum describes the mode to be used when writing a characteristic value.
\inmodule QtCore
Definition qobject.h:90
void moveToThread(QThread *thread)
Changes the thread affinity for this object and its children.
Definition qobject.cpp:1606
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1561
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
void deleteLater()
\threadsafe
Definition qobject.cpp:2352
\inmodule QtCore
Definition qpointer.h:18
bool remove(const T &value)
Definition qset.h:63
void clear()
Definition qset.h:61
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtCore
bool isNull() const noexcept
Returns true if this object refers to \nullptr.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QByteArray toUtf8() const &
Definition qstring.h:563
void start(Priority=InheritPriority)
Definition qthread.cpp:923
static QThread * currentThread()
Definition qthread.cpp:966
void finished(QPrivateSignal)
void quit()
Definition qthread.cpp:935
void started(QPrivateSignal)
void deviceConnected(ComPtr< IBluetoothLEDevice > device, ComPtr< IGattSession > session)
QWinRTLowEnergyConnectionHandler(const QBluetoothAddress &address)
void errorOccurred(const QString &error)
void errorOccured(const QString &error)
void charListObtained(const QBluetoothUuid &service, QHash< QLowEnergyHandle, QLowEnergyServicePrivate::CharData > charList, QList< QBluetoothUuid > indicateChars, QLowEnergyHandle startHandle, QLowEnergyHandle endHandle)
QLowEnergyService::DiscoveryMode mMode
QWinRTLowEnergyServiceHandler(const QBluetoothUuid &service, const ComPtr< IGattDeviceService3 > &deviceService, QLowEnergyService::DiscoveryMode mode)
ComPtr< IGattDeviceService3 > mDeviceService
QHash< QLowEnergyHandle, QLowEnergyServicePrivate::CharData > mCharacteristicList
#define this
Definition dialogs.cpp:9
void qErrnoWarning(const char *msg,...)
Token token
Definition keywords.cpp:444
Combined button and popup list for selecting options.
Q_CORE_EXPORT QtJniTypes::Service service()
@ QueuedConnection
constexpr Initialization Uninitialized
quint16 QLowEnergyHandle
Definition qbluetooth.h:42
static QByteArray byteArrayFromBuffer(const IBuffer &buffer)
static const QCssKnownValue properties[NumProperties - 1]
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
constexpr T qToLittleEndian(T source)
Definition qendian.h:176
Q_DECL_COLD_FUNCTION Q_CORE_EXPORT QString qt_error_string(int errorCode=-1)
#define qWarning
Definition qlogging.h:162
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharConfigDescriptorResult
#define EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, message)
QT_BEGIN_NAMESPACE typedef ITypedEventHandler< BluetoothLEDevice *, IInspectable * > StatusHandler
#define CHECK_HR_AND_SET_SERVICE_ERROR(hr, msg, service, error, ret)
static QByteArray byteArrayFromGattResult(const ComPtr< IGattReadResult > &gattResult, bool isWCharString=false)
static void closeDeviceService(ComPtr< T > service)
#define DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr, msg)
#define CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, msg, ret)
ITypedEventHandler< GattSession *, IInspectable * > MtuHandler
GattReadClientCharacteristicConfigurationDescriptorResult ClientCharConfigDescriptorResult
static constexpr qint64 kMaxConnectTimeout
ITypedEventHandler< GattCharacteristic *, GattValueChangedEventArgs * > ValueChangedHandler
#define WARN_AND_CONTINUE_IF_FAILED(hr, msg)
#define EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr)
void registerQLowEnergyControllerMetaType()
static const QMetaObjectPrivate * priv(const uint *data)
GLuint64 GLenum void * handle
GLenum mode
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint GLenum GLsizei length
GLenum GLenum GLsizei count
GLenum GLuint buffer
GLenum GLuint GLsizei const GLenum * props
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint entry
GLuint GLuint64EXT address
GLsizei const void * pointer
Definition qopenglext.h:384
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
GLuint GLenum option
QT_BEGIN_NAMESPACE typedef void(* Callback)(QQmlNotifierEndpoint *, void **)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int(*) void arg)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define Q_OBJECT
#define slots
#define signals
#define emit
#define Q_UNIMPLEMENTED()
unsigned int quint32
Definition qtypes.h:45
unsigned short quint16
Definition qtypes.h:43
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
long HRESULT
if(qFloatDistance(a, b)<(1<< 7))
[0]
settings remove("monkey")
QDeadlineTimer deadline(30s)
myObject disconnect()
[26]
QJSValueList args
QHash< QLowEnergyHandle, DescData > descriptorList
QLowEnergyCharacteristic::PropertyTypes properties