Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
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#include <QtCore/qpointer.h>
18#include <QtCore/QAbstractEventDispatcher>
19#include <QtCore/QDeadlineTimer>
20
21#include <functional>
22#include <type_traits>
23
24#include <winrt/Windows.Foundation.h>
25#include <winrt/Windows.Foundation.Collections.h>
26#include <winrt/Windows.Foundation.Metadata.h>
27#include <winrt/Windows.Devices.Enumeration.h>
28#include <winrt/Windows.Devices.Bluetooth.h>
29#include <winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h>
30#include <winrt/Windows.Storage.Streams.h>
31
32#include <windows.devices.bluetooth.h>
33#include <windows.devices.bluetooth.genericattributeprofile.h>
34
35using namespace winrt;
36using namespace winrt::Windows::Foundation;
37using namespace winrt::Windows::Foundation::Collections;
38using namespace winrt::Windows::Devices;
39using namespace winrt::Windows::Devices::Bluetooth;
40using namespace winrt::Windows::Devices::Bluetooth::GenericAttributeProfile;
41using namespace winrt::Windows::Devices::Enumeration;
42using namespace winrt::Windows::Storage::Streams;
43
49
50template<typename E,
51 std::enable_if_t<std::is_enum_v<E>, int> = 0>
52inline constexpr std::underlying_type_t<E> bitwise_and(E x, E y)
53{
54 return static_cast<std::underlying_type_t<E>>(x) & static_cast<std::underlying_type_t<E>>(y);
55}
56
57template<typename T, typename E,
58 std::enable_if_t<std::conjunction_v<std::is_integral<T>, std::is_enum<E>>, int> = 0>
59inline constexpr T bitwise_and(T x, E y)
60{
61 return x & static_cast<std::underlying_type_t<E>>(y);
62}
63
64template<typename E,
65 std::enable_if_t<std::is_enum_v<E>, int> = 0>
66inline constexpr std::underlying_type_t<E> bitwise_or(E x, E y)
67{
68 return static_cast<std::underlying_type_t<E>>(x) | static_cast<std::underlying_type_t<E>>(y);
69}
70
71template<typename T, typename E,
72 std::enable_if_t<std::conjunction_v<std::is_integral<T>, std::is_enum<E>>, int> = 0>
73inline constexpr T bitwise_or(T x, E y)
74{
75 return x | static_cast<std::underlying_type_t<E>>(y);
76}
77
78template<typename T, typename E,
79 std::enable_if_t<std::conjunction_v<std::is_integral<T>, std::is_enum<E>>, int> = 0>
80inline constexpr T &bitwise_or_equal(T &a, const E &b)
81{
82 a |= static_cast<std::underlying_type_t<E>>(b);
83 return a;
84}
85
86#define ENUM_BITWISE_OPS(E) inline
87 constexpr std::underlying_type_t<E> operator&(E x, E y) { return bitwise_and<E>(x, y); } template
88
89 <typename T> inline
90 constexpr T operator&(T x, E y) { return bitwise_and<T, E>(x, y); } template
91
92 <typename T> inline
93 constexpr T operator&(E x, T y) { return bitwise_and<T, E>(y, x); } inline
94
95 constexpr std::underlying_type_t<E> operator|(E x, E y) { return bitwise_or<E>(x, y); } template
96
97 <typename T> inline
98 constexpr T operator|(T x, E y) { return bitwise_or<T, E>(x, y); } template
99
100 <typename T> inline
101 constexpr T operator|(E x, T y) { return bitwise_or<T, E>(y, x); } template
102
103 <typename T> inline
104 constexpr T &operator |=(T &a, const E &b) { return bitwise_or_equal(a, b); }
105
106QT_BEGIN_NAMESPACE
107
108ENUM_BITWISE_OPS(GattClientCharacteristicConfigurationDescriptorValue)
109
111
112using GlobalCondition = std::function<bool()>;
113static constexpr bool never() { return false; }
114
115template <typename T>
116static T await(IAsyncOperation<T> asyncInfo, GlobalCondition canceled = never, int timeout = 5000)
117{
118 QDeadlineTimer awaitTime(timeout);
119 auto *dispatch = QAbstractEventDispatcher::instance();
120 if (!dispatch)
121 dispatch = QCoreApplication::eventDispatcher();
122 do {
123 QThread::yieldCurrentThread();
124 if (asyncInfo.Status() != winrt::AsyncStatus::Started) {
125 check_hresult(asyncInfo.ErrorCode());
126 return asyncInfo.GetResults();
127 }
128 if (dispatch)
129 dispatch->processEvents(QEventLoop::AllEvents);
130 } while (!canceled() && !awaitTime.hasExpired());
131 asyncInfo.Cancel();
132 throw hresult_error(E_ABORT);
133}
134
135constexpr int timeout_infinity = -1;
136
137template <typename T>
138static inline T await_forever(IAsyncOperation<T> asyncInfo, GlobalCondition canceled = never)
139{
140 return await(asyncInfo, canceled, timeout_infinity);
141}
142
143#define WARN_AND_CONTINUE(msg)
144 {
145 qCWarning(QT_BT_WINDOWS) << msg;
146 continue;
147 }
148
149#define DEC_CHAR_COUNT_AND_CONTINUE(msg)
150 {
151 qCWarning(QT_BT_WINDOWS) << msg;
152 --mCharacteristicsCountToBeDiscovered;
153 continue;
154 }
155
156#define RETURN_SERVICE_ERROR(msg, service, error)
157 {
158 qCDebug(QT_BT_WINDOWS) << msg;
159 service->setError(error);
160 return;
161 }
162
163#define RETURN_FALSE(msg)
164 {
165 qErrnoWarning(msg);
166 return false;
167 }
168
169#define RETURN_MSG(msg)
170 {
171 qErrnoWarning(msg);
172 return;
173 }
174
175Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
176Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS_SERVICE_THREAD)
177
178static constexpr qint64 kMaxConnectTimeout = 20000; // 20 sec
179
180static QByteArray byteArrayFromBuffer(IBuffer buffer, bool isWCharString = false)
181{
182 if (!buffer) {
183 qErrnoWarning("nullptr passed to byteArrayFromBuffer");
184 return QByteArray();
185 }
186 qsizetype size = SAFE(buffer.Length());
187 if (!size)
188 return QByteArray();
189 if (isWCharString) {
190 QString valueString = QString::fromUtf16(
191 reinterpret_cast<char16_t *>(buffer.data())).left(size / 2);
192 return valueString.toUtf8();
193 }
194 return QByteArray(reinterpret_cast<char *>(buffer.data()), size);
195}
196
197static QByteArray byteArrayFromGattResult(GattReadResult gattResult,
198 bool isWCharString = false)
199{
200 auto buffer = SAFE(gattResult.Value());
201 if (!buffer) {
202 qCWarning(QT_BT_WINDOWS) << "Could not obtain buffer from GattReadResult";
203 return QByteArray();
204 }
205 return byteArrayFromBuffer(buffer, isWCharString);
206}
207
209{
211public:
219
226
227public slots:
229 {
230 auto exitCondition = [this]() { return mAbortRequested; };
231
233 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
234
238 return emitErrorAndQuitThread(QLatin1String("Could not obtain char list"));
239
241 return emitErrorAndQuitThread(QLatin1String("Could not obtain char list"));
242
244 if (!characteristics)
245 return emitErrorAndQuitThread(QLatin1String("Could not obtain char list"));
246
249 return emitErrorAndQuitThread(QLatin1String("Could not obtain char list"));
250
252 for (uint i = 0; !mAbortRequested && (i < characteristicsCount); ++i) {
254 if (!characteristic) {
255 qCWarning(QT_BT_WINDOWS) << "Could not obtain characteristic at" << i;
257 continue;
258 }
259
260 // For some strange reason, Windows doesn't discover descriptors of characteristics (if not paired).
261 // Qt API assumes that all characteristics and their descriptors are discovered in one go.
262 // So we start 'GetDescriptorsAsync' for each discovered characteristic and finish only
263 // when GetDescriptorsAsync for all characteristics return.
265 if (!descResult)
266 DEC_CHAR_COUNT_AND_CONTINUE("Could not obtain descriptor read result");
267
268 uint16_t handle = 0;
270 DEC_CHAR_COUNT_AND_CONTINUE("Could not obtain characteristic's attribute handle");
271
274 if (mStartHandle == 0 || mStartHandle > handle)
276 if (mEndHandle == 0 || mEndHandle < handle)
278
280 DEC_CHAR_COUNT_AND_CONTINUE("Could not read characteristic UUID");
281
284 DEC_CHAR_COUNT_AND_CONTINUE("Could not read characteristic properties");
285
289
290 GattReadResult readResult = nullptr;
292 DEC_CHAR_COUNT_AND_CONTINUE("Could not read characteristic");
293
294 if (!readResult)
295 qCWarning(QT_BT_WINDOWS) << "Characteristic read result is null";
296 else
298 }
300
302 DEC_CHAR_COUNT_AND_CONTINUE("Descriptor operation failed");
303
305 if (!descriptors)
306 DEC_CHAR_COUNT_AND_CONTINUE("Could not obtain list of descriptors");
309 DEC_CHAR_COUNT_AND_CONTINUE("Could not obtain list of descriptors' size");
310 for (uint j = 0; !mAbortRequested && (j < descriptorCount); ++j) {
313 if (!descriptor)
314 WARN_AND_CONTINUE("Could not access descriptor");
317 WARN_AND_CONTINUE("Could not get descriptor handle");
319 WARN_AND_CONTINUE("Could not get descriptor UUID");
324 if (!readResult)
325 WARN_AND_CONTINUE("Could not read descriptor value");
328 WARN_AND_CONTINUE("Could not get descriptor value from result");
329 quint16 result = 0;
330 bool correct = false;
333 correct = true;
334 }
337 correct = true;
338 }
340 correct = true;
341 if (!correct)
342 continue;
343
346 }
348 } else {
350
352 if (!readResult)
353 WARN_AND_CONTINUE("Could not read descriptor value");
355 }
356 }
358 }
359
362 }
364 }
365
367 {
368 mAbortRequested = true;
369 }
370
371private:
373 void emitErrorAndQuitThread(const QString &error);
374
375public:
384 bool mAbortRequested = false;
385
386signals:
391 void errorOccured(const QString &error);
392};
393
394void QWinRTLowEnergyServiceHandler::checkAllCharacteristicsDiscovered()
395{
396 if (!mAbortRequested && (mCharacteristicsCountToBeDiscovered == 0)) {
397 emit charListObtained(mService, mCharacteristicList, mIndicateChars,
398 mStartHandle, mEndHandle);
399 }
400 QThread::currentThread()->quit();
401}
402
403void QWinRTLowEnergyServiceHandler::emitErrorAndQuitThread(const QString &error)
404{
405 mAbortRequested = true; // so that the service is closed during cleanup
406 emit errorOccured(error);
407 QThread::currentThread()->quit();
408}
409
411{
413public:
415 {
416 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
417 // This should be checked before the handler is created
419 }
421 {
422 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
423 mDevice = nullptr;
424 mGattSession = nullptr;
425 // To close the COM library gracefully, each successful call to
426 // CoInitialize, including those that return S_FALSE, must be balanced
427 // by a corresponding call to CoUninitialize.
428 if (mComInitialized)
430 }
431
432public slots:
435
436signals:
438 void errorOccurred(const QString &error);
439
440private:
441 void connectToPairedDevice();
442 void connectToUnpairedDevice();
443 void emitErrorAndQuitThread(const QString &error);
444 void emitErrorAndQuitThread(const char *error);
445 void emitConnectedAndQuitThread();
446
447 BluetoothLEDevice mDevice = nullptr;
448 GattSession mGattSession = nullptr;
449 const QBluetoothAddress mAddress;
450 bool mAbortConnection = false;
451 bool mComInitialized = false;
452};
453
454void QWinRTLowEnergyConnectionHandler::connectToDevice()
455{
456 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
457 mComInitialized = TRY(winrt::init_apartment());
458
459 auto earlyExit = [this]() { return mAbortConnection; };
460 if (!(mDevice = SAFE(await(BluetoothLEDevice::FromBluetoothAddressAsync(mAddress.toUInt64()), earlyExit))))
461 return emitErrorAndQuitThread("Could not find LE device from address");
462
463 // get GattSession: 1. get device id
464 BluetoothDeviceId deviceId = SAFE(mDevice.BluetoothDeviceId());
465 if (!deviceId)
466 return emitErrorAndQuitThread("Could not get device id");
467
468 // get GattSession: 3. get session
469 if (!(mGattSession = SAFE(await(GattSession::FromDeviceIdAsync(deviceId), earlyExit))))
470 return emitErrorAndQuitThread("Could not get GattSession from id");
471
472 BluetoothConnectionStatus status;
473 if (!TRY(status = mDevice.ConnectionStatus()))
474 return emitErrorAndQuitThread("Could not get connection status");
475 if (status == BluetoothConnectionStatus::Connected)
476 return emitConnectedAndQuitThread();
477
478 QBluetoothLocalDevice localDevice;
479 QBluetoothLocalDevice::Pairing pairing = localDevice.pairingStatus(mAddress);
480 if (pairing == QBluetoothLocalDevice::Unpaired)
481 connectToUnpairedDevice();
482 else
483 connectToPairedDevice();
484}
485
487{
488 mAbortConnection = true;
489 // Disconnect from the QLowEnergyControllerPrivateWinRT, so that it does
490 // not get notifications. It's absolutely fine to keep doing smth in
491 // background, as multiple connections to the same device should be handled
492 // correctly by OS.
493 disconnect(this, &QWinRTLowEnergyConnectionHandler::deviceConnected, nullptr, nullptr);
494 disconnect(this, &QWinRTLowEnergyConnectionHandler::errorOccurred, nullptr, nullptr);
495}
496
497void QWinRTLowEnergyConnectionHandler::connectToPairedDevice()
498{
499 auto earlyExit = [this]() { return mAbortConnection; };
500 QDeadlineTimer deadline(kMaxConnectTimeout);
501 while (!mAbortConnection && !deadline.hasExpired()) {
502
503 auto deviceServicesResult = SAFE(await(mDevice.GetGattServicesAsync(), earlyExit));
504 if (!deviceServicesResult)
505 return emitErrorAndQuitThread("Could not obtain services");
506
507 if (!SAFE(deviceServicesResult.Status() == GattCommunicationStatus::Success))
508 return emitErrorAndQuitThread("Service operation failed");
509
510 auto deviceServices = SAFE(deviceServicesResult.Services());
511 if (!deviceServices)
512 return emitErrorAndQuitThread("Could not obtain list of services");
513
514 uint serviceCount;
515 if (!TRY(serviceCount = deviceServices.Size()))
516 return emitErrorAndQuitThread("Could not obtain size of list of services");
517 if (serviceCount == 0)
518 return emitErrorAndQuitThread("Found devices without services");
519
520 // Windows automatically connects to the device as soon as a service value is read/written.
521 // Thus we read one value in order to establish the connection.
522 for (uint i = 0; i < serviceCount; ++i) {
523
524 auto service = SAFE(deviceServices.GetAt(i));
525 if (!service)
526 return emitErrorAndQuitThread("Could not obtain service");
527
528 auto characteristicsResult = SAFE(await(service.GetCharacteristicsAsync(), earlyExit));
529 if (!characteristicsResult)
530 return emitErrorAndQuitThread("Could not obtain characteristic");
531
532 if (!SAFE(characteristicsResult.Status() == GattCommunicationStatus::Success)) {
533 qCWarning(QT_BT_WINDOWS) << "Characteristic operation failed";
534 break;
535 }
536
537 IVectorView<GattCharacteristic> characteristics = nullptr;
538 auto hr = HR(characteristics = characteristicsResult.Characteristics());
539 if (hr == E_ACCESSDENIED) {
540 // Everything will work as expected up until this point if the
541 // manifest capabilties for bluetooth LE are not set.
542 return emitErrorAndQuitThread("Could not obtain characteristic list. "
543 "Please check your manifest capabilities");
544 } else if (FAILED(hr) || !characteristics) {
545 return emitErrorAndQuitThread("Could not obtain characteristic list.");
546 }
547
548 uint characteristicsCount;
549 if (!TRY(characteristicsCount = characteristics.Size()))
550 return emitErrorAndQuitThread("Could not obtain size of characteristic list.");
551 for (uint j = 0; j < characteristicsCount; ++j) {
552
553 auto characteristic = SAFE(characteristics.GetAt(j));
554 if (!characteristic)
555 return emitErrorAndQuitThread("Could not get characteristic");
556 GattReadResult result = nullptr;
557 hr = HR(result = await(
558 characteristic.ReadValueAsync(BluetoothCacheMode::Uncached), earlyExit));
559 if (hr == E_ILLEGAL_METHOD_CALL) {
560 // E_ILLEGAL_METHOD_CALL will be the result for a device, that is not reachable at
561 // the moment. In this case we should jump back into the outer loop and keep trying.
562 break;
563 } else if (FAILED(hr) || !result) {
564 return emitErrorAndQuitThread("Could not read characteristic value");
565 }
566
567 auto buffer = SAFE(result.Value());
568 if (!buffer) {
569 qCDebug(QT_BT_WINDOWS) << "Problem reading value";
570 break;
571 }
572
573 emitConnectedAndQuitThread();
574 return;
575 }
576 }
577 }
578 // If we got here because of mAbortConnection == true, the error message
579 // will not be delivered, so it does not matter. But we need to terminate
580 // the thread anyway!
581 emitErrorAndQuitThread("Connect to device failed due to timeout!");
582}
583
584void QWinRTLowEnergyConnectionHandler::connectToUnpairedDevice()
585{
586 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
587
588 auto earlyExit = [this]() { return mAbortConnection; };
589 QDeadlineTimer deadline(kMaxConnectTimeout);
590 while (!mAbortConnection && !deadline.hasExpired()) {
591
592 auto deviceServicesResult = SAFE(await_forever(mDevice.GetGattServicesAsync(), earlyExit));
593 if (!deviceServicesResult)
594 return emitErrorAndQuitThread("Could not obtain services");
595
596 GattCommunicationStatus commStatus;
597 if (!TRY(commStatus = deviceServicesResult.Status()))
598 return emitErrorAndQuitThread("Could not obtain comm status");
599 if (commStatus == GattCommunicationStatus::Unreachable)
600 continue;
601 if (commStatus != GattCommunicationStatus::Success)
602 return emitErrorAndQuitThread("Service operation failed");
603
604 emitConnectedAndQuitThread();
605 return;
606 }
607 // If we got here because of mAbortConnection == true, the error message
608 // will not be delivered, so it does not matter. But we need to terminate
609 // the thread anyway!
610 emitErrorAndQuitThread("Connect to device failed due to timeout!");
611}
612
613void QWinRTLowEnergyConnectionHandler::emitErrorAndQuitThread(const QString &error)
614{
615 emit errorOccurred(error);
616 QThread::currentThread()->quit();
617}
618
619void QWinRTLowEnergyConnectionHandler::emitErrorAndQuitThread(const char *error)
620{
621 emitErrorAndQuitThread(QString::fromUtf8(error));
622}
623
624void QWinRTLowEnergyConnectionHandler::emitConnectedAndQuitThread()
625{
626 emit deviceConnected(mDevice.as<abi::BluetoothLEDevice>(), mGattSession.as<abi::GattSession>());
627 QThread::currentThread()->quit();
628}
629
630QLowEnergyControllerPrivateWinRT::QLowEnergyControllerPrivateWinRT()
632{
634 connect(this, &QLowEnergyControllerPrivateWinRT::characteristicChanged,
635 this, &QLowEnergyControllerPrivateWinRT::handleCharacteristicChanged,
636 Qt::QueuedConnection);
637}
638
639QLowEnergyControllerPrivateWinRT::~QLowEnergyControllerPrivateWinRT()
640{
641 unregisterFromStatusChanges();
642 unregisterFromValueChanges();
643}
644
645void QLowEnergyControllerPrivateWinRT::init()
646{
647}
648
649void QLowEnergyControllerPrivateWinRT::connectToDevice()
650{
651 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
652 if (remoteDevice.isNull()) {
653 qCWarning(QT_BT_WINDOWS) << "Invalid/null remote device address";
654 setError(QLowEnergyController::UnknownRemoteDeviceError);
655 return;
656 }
657 setState(QLowEnergyController::ConnectingState);
658
659 QWinRTLowEnergyConnectionHandler *worker = new QWinRTLowEnergyConnectionHandler(remoteDevice);
660 QThread *thread = new QThread;
661 worker->moveToThread(thread);
662 connect(this, &QLowEnergyControllerPrivateWinRT::abortConnection, worker,
664 connect(thread, &QThread::started, worker, &QWinRTLowEnergyConnectionHandler::connectToDevice);
665 connect(thread, &QThread::finished, worker, &QObject::deleteLater);
666 connect(worker, &QObject::destroyed, thread, &QObject::deleteLater);
667 connect(worker, &QWinRTLowEnergyConnectionHandler::errorOccurred, this,
668 [this](const QString &msg) { handleConnectionError(msg.toUtf8().constData()); });
669 connect(worker, &QWinRTLowEnergyConnectionHandler::deviceConnected, this,
670 [this](com_ptr<abi::BluetoothLEDevice> device, com_ptr<abi::GattSession> session) {
671 if (!device || !session) {
672 handleConnectionError("Failed to get device or gatt service");
673 return;
674 }
675 mDevice = device.as<BluetoothLEDevice>();
676 mGattSession = session.as<GattSession>();
677
678 if (!registerForStatusChanges() || !registerForMtuChanges()) {
679 handleConnectionError("Failed to register for changes");
680 return;
681 }
682
683 Q_Q(QLowEnergyController);
684 setState(QLowEnergyController::ConnectedState);
685 emit q->connected();
686 });
687 thread->start();
688}
689
690void QLowEnergyControllerPrivateWinRT::disconnectFromDevice()
691{
692 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
693 Q_Q(QLowEnergyController);
694 setState(QLowEnergyController::ClosingState);
695 emit abortConnection();
696 unregisterFromValueChanges();
697 unregisterFromStatusChanges();
698 unregisterFromMtuChanges();
699 clearAllServices();
700 mGattSession = nullptr;
701 mDevice = nullptr;
702 setState(QLowEnergyController::UnconnectedState);
703 emit q->disconnected();
704}
705
706bool QLowEnergyControllerPrivateWinRT::getNativeService(const QBluetoothUuid &serviceUuid,
707 NativeServiceCallback callback)
708{
709 if (m_openedServices.contains(serviceUuid)) {
710 callback(m_openedServices.value(serviceUuid, nullptr));
711 return true;
712 }
713
714 auto servicesResultOperation = SAFE(mDevice.GetGattServicesForUuidAsync(GUID(serviceUuid)));
715 if (!servicesResultOperation)
716 RETURN_FALSE("Could not start async services request");
717
718 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(this);
719 bool ok = TRY(servicesResultOperation.Completed(
720 [thisPtr, callback, &serviceUuid](
721 IAsyncOperation<GattDeviceServicesResult> const &op, winrt::AsyncStatus const status)
722 {
723 if (!thisPtr) {
724 qCWarning(QT_BT_WINDOWS) << "LE controller was removed while getting native service";
725 return;
726 }
727
728 if (status != winrt::AsyncStatus::Completed) {
729 qCDebug(QT_BT_WINDOWS) << "Failed to get result of async service request";
730 return;
731 }
732
733 auto result = SAFE(op.GetResults());
734 if (!result)
735 RETURN_MSG("Failed to get result of async service request");
736
737 auto services = SAFE(result.Services());
738 if (!services)
739 RETURN_MSG("Failed to extract services from the result");
740
741 uint servicesCount = 0;
742 if (!TRY(servicesCount = services.Size()))
743 RETURN_MSG("Failed to extract services count");
744
745 if (servicesCount > 0) {
746 if (servicesCount > 1) {
747 qWarning() << "getNativeService: more than one service detected for UUID"
748 << serviceUuid << "The first service will be used.";
749 }
750
751 auto service = SAFE(services.GetAt(0));
752 if (!service) {
753 qCDebug(QT_BT_WINDOWS) << "Could not obtain native service for Uuid"
754 << serviceUuid;
755 } else {
756 thisPtr->m_openedServices.insert(serviceUuid, service);
757 callback(service); // Use the service in a custom callback
758 }
759 } else {
760 qCWarning(QT_BT_WINDOWS) << "No services found for Uuid" << serviceUuid;
761 }
762 }));
763
764 return ok;
765}
766
767bool QLowEnergyControllerPrivateWinRT::getNativeCharacteristic(
768 const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid,
769 NativeCharacteristicCallback callback)
770{
771 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(this);
772 auto serviceCallback = [thisPtr, callback, charUuid](GattDeviceService service) {
773
774 auto characteristicRequestOp = SAFE(service.GetCharacteristicsForUuidAsync(GUID(charUuid)));
775 if (!characteristicRequestOp)
776 RETURN_MSG("Could not start async characteristics request");
777
778 TRY(characteristicRequestOp.Completed(
779 [thisPtr, callback](
780 IAsyncOperation<GattCharacteristicsResult> const &op, winrt::AsyncStatus const status)
781 {
782 if (!thisPtr)
783 return;
784
785 if (status != winrt::AsyncStatus::Completed) {
786 qCDebug(QT_BT_WINDOWS) << "Failed to get result of async characteristic "
787 "operation";
788 return;
789 }
790
791 auto result = SAFE(op.GetResults());
792 if (!result)
793 RETURN_MSG("Failed to get result of async characteristic operation");
794
795 GattCommunicationStatus commStatus;
796 if (!TRY(commStatus = result.Status()) || commStatus != GattCommunicationStatus::Success) {
797 qErrnoWarning("Native characteristic operation failed.");
798 return;
799 }
800
801 auto characteristics = SAFE(result.Characteristics());
802 if (!characteristics)
803 RETURN_MSG("Could not obtain characteristic list.");
804
805 uint size;
806 if (!TRY(size = characteristics.Size()))
807 RETURN_MSG("Could not obtain characteristic list's size.");
808
809 if (size != 1)
810 qErrnoWarning("More than 1 characteristic found.");
811
812 auto characteristic = SAFE(characteristics.GetAt(0));
813 if (!characteristic)
814 RETURN_MSG("Could not obtain first characteristic for service");
815
816 callback(characteristic); // use the characteristic in a custom callback
817 }));
818 };
819
820 if (!getNativeService(serviceUuid, serviceCallback)) {
821 qCDebug(QT_BT_WINDOWS) << "Failed to get native service for" << serviceUuid;
822 return false;
823 }
824
825 return true;
826}
827
828void QLowEnergyControllerPrivateWinRT::registerForValueChanges(const QBluetoothUuid &serviceUuid,
829 const QBluetoothUuid &charUuid)
830{
831 qCDebug(QT_BT_WINDOWS) << "Registering characteristic" << charUuid << "in service"
832 << serviceUuid << "for value changes";
833 for (const ValueChangedEntry &entry : std::as_const(mValueChangedTokens)) {
834 GUID guuid{ 0 };
835 if (!TRY(guuid = entry.characteristic.Uuid()))
836 WARN_AND_CONTINUE("Could not obtain characteristic's Uuid");
837 if (QBluetoothUuid(guuid) == charUuid)
838 return;
839 }
840
841 auto callback = [this, charUuid, serviceUuid](GattCharacteristic characteristic) {
842 winrt::event_token token;
843 if (!TRY(token = characteristic.ValueChanged({ this, &QLowEnergyControllerPrivateWinRT::onValueChange })))
844 RETURN_MSG("Could not register characteristic for value changes");
845
846 mValueChangedTokens.append(ValueChangedEntry(characteristic, token));
847 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charUuid << "in service"
848 << serviceUuid << "registered for value changes";
849 };
850
851 if (!getNativeCharacteristic(serviceUuid, charUuid, callback)) {
852 qCDebug(QT_BT_WINDOWS).nospace() << "Could not obtain native characteristic "
853 << charUuid << " from service " << serviceUuid
854 << ". Qt will not be able to signal"
855 << " changes for this characteristic.";
856 }
857}
858
859void QLowEnergyControllerPrivateWinRT::unregisterFromValueChanges()
860{
861 qCDebug(QT_BT_WINDOWS) << "Unregistering " << mValueChangedTokens.size() << " value change tokens";
862 for (const ValueChangedEntry &entry : std::as_const(mValueChangedTokens)) {
863 if (!entry.characteristic) {
864 qCWarning(QT_BT_WINDOWS) << "Unregistering from value changes for characteristic failed."
865 << "Characteristic has been deleted";
866 continue;
867 }
868 if (!TRY(entry.characteristic.ValueChanged(entry.token)))
869 qCWarning(QT_BT_WINDOWS) << "Unregistering from value changes for characteristic failed.";
870 }
871 mValueChangedTokens.clear();
872}
873
874void QLowEnergyControllerPrivateWinRT::onValueChange(GattCharacteristic characteristic, GattValueChangedEventArgs const &args)
875{
876 quint16 handle = 0;
877 if (!TRY(handle = characteristic.AttributeHandle()))
878 RETURN_MSG("Could not obtain characteristic's handle");
879
880 auto buffer = SAFE(args.CharacteristicValue());
881 if (!buffer)
882 RETURN_MSG("Could not obtain characteristic's value");
883
884 emit characteristicChanged(handle, byteArrayFromBuffer(buffer));
885}
886
887bool QLowEnergyControllerPrivateWinRT::registerForMtuChanges()
888{
889 if (!mDevice || !mGattSession)
890 return false;
891 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
892 if (!TRY(mMtuChangedToken = mGattSession.MaxPduSizeChanged({ this, &QLowEnergyControllerPrivateWinRT::onMtuChange })))
893 RETURN_FALSE("Could not add MTU callback");
894 return true;
895}
896
897void QLowEnergyControllerPrivateWinRT::unregisterFromMtuChanges()
898{
899 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
900 if (mDevice && mGattSession && mMtuChangedToken) {
901 TRY(mGattSession.MaxPduSizeChanged(mMtuChangedToken));
902 mMtuChangedToken = { 0 };
903 }
904}
905
906void QLowEnergyControllerPrivateWinRT::onMtuChange(GattSession session, winrt::IInspectable args)
907{
908 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
909
910 if (session != mGattSession) {
911 qCWarning(QT_BT_WINDOWS) << "Got MTU changed event for wrong or outdated GattSession.";
912 } else {
913 Q_Q(QLowEnergyController);
914 emit q->mtuChanged(mtu());
915 }
916}
917
918bool QLowEnergyControllerPrivateWinRT::registerForStatusChanges()
919{
920 if (!mDevice)
921 return false;
922
923 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
924
925 if (!TRY(mStatusChangedToken = mDevice.ConnectionStatusChanged({ this, &QLowEnergyControllerPrivateWinRT::onStatusChange })))
926 RETURN_FALSE("Could not add status callback");
927 return true;
928}
929
930void QLowEnergyControllerPrivateWinRT::unregisterFromStatusChanges()
931{
932 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
933 if (mDevice && mStatusChangedToken) {
934 TRY(mDevice.ConnectionStatusChanged(mStatusChangedToken));
935 mStatusChangedToken = { 0 };
936 }
937}
938
939void QLowEnergyControllerPrivateWinRT::onStatusChange(BluetoothLEDevice dev, winrt::IInspectable args)
940{
941 Q_Q(QLowEnergyController);
942
943 BluetoothConnectionStatus status;
944 if (!TRY(status = dev.ConnectionStatus()))
945 RETURN_MSG("Could not obtain connection status");
946
947 if (state == QLowEnergyController::ConnectingState
948 && status == BluetoothConnectionStatus::Connected) {
949 setState(QLowEnergyController::ConnectedState);
950 emit q->connected();
951 } else if (state != QLowEnergyController::UnconnectedState
952 && status == BluetoothConnectionStatus::Disconnected) {
954 unregisterFromValueChanges();
955 unregisterFromStatusChanges();
956 unregisterFromMtuChanges();
957 mGattSession = nullptr;
958 mDevice = nullptr;
959 setError(QLowEnergyController::RemoteHostClosedError);
960 setState(QLowEnergyController::UnconnectedState);
961 emit q->disconnected();
962 }
963}
964
965void QLowEnergyControllerPrivateWinRT::obtainIncludedServices(QSharedPointer<QLowEnergyServicePrivate> servicePointer, GattDeviceService service)
966{
967 Q_Q(QLowEnergyController);
968
969 auto result = SAFE(await(service.GetIncludedServicesAsync()));
970 if (!result)
971 RETURN_MSG("Could not obtain included services");
972
973 // The device can be disconnected by the time we return from await()
974 if (state != QLowEnergyController::DiscoveringState)
975 return;
976
977 GattCommunicationStatus status;
978 if (!TRY(status = result.Status())) {
979 qErrnoWarning("Could not obtain list of included services");
980 return;
981 }
982
983 auto includedServices = SAFE(result.Services());
984 if (!includedServices)
985 RETURN_MSG("Could not obtain service list");
986
987 uint count;
988 if (!TRY(count = includedServices.Size()))
989 RETURN_MSG("Could not obtain service list's size");
990
991 for (uint i = 0; i < count; ++i) {
992
993 auto includedService = SAFE(includedServices.GetAt(i));
994 if (!includedService)
995 WARN_AND_CONTINUE("Could not obtain service from list");
996
997 GUID guuid;
998 if (!TRY(guuid = includedService.Uuid()))
999 WARN_AND_CONTINUE("Could not obtain included service's Uuid");
1000
1001 const QBluetoothUuid includedUuid(guuid);
1002 QSharedPointer<QLowEnergyServicePrivate> includedPointer;
1003 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__
1004 << "Changing service pointer from thread"
1005 << QThread::currentThread();
1006 if (serviceList.contains(includedUuid)) {
1007 includedPointer = serviceList.value(includedUuid);
1008 } else {
1010 priv->uuid = includedUuid;
1011 priv->setController(this);
1012
1013 includedPointer = QSharedPointer<QLowEnergyServicePrivate>(priv);
1014 serviceList.insert(includedUuid, includedPointer);
1015 }
1016 includedPointer->type |= QLowEnergyService::IncludedService;
1017 servicePointer->includedServices.append(includedUuid);
1018
1019 obtainIncludedServices(includedPointer, includedService);
1020
1021 emit q->serviceDiscovered(includedUuid);
1022 }
1023}
1024
1025void QLowEnergyControllerPrivateWinRT::onServiceDiscoveryFinished(IAsyncOperation<GattDeviceServicesResult> const &op, winrt::AsyncStatus status)
1026{
1027 // Check if the device is in the proper state, because it can already be
1028 // disconnected when the callback arrives.
1029 // Also the callback can theoretically come when the connection is
1030 // reestablisheed again (for example, if the user quickly clicks
1031 // "Disconnect" and then "Connect" again in some UI). But we can probably
1032 // omit such details, as we are connecting to the same device anyway.
1033 if (state != QLowEnergyController::DiscoveringState)
1034 return;
1035
1036 Q_Q(QLowEnergyController);
1037 if (status != winrt::AsyncStatus::Completed) {
1038 qCDebug(QT_BT_WINDOWS) << "Could not obtain services";
1039 return;
1040 }
1041
1042 auto result = SAFE(op.GetResults());
1043 if (!result)
1044 return handleConnectionError("Could not obtain service discovery result");
1045
1046 GattCommunicationStatus commStatus;
1047 if (!TRY(commStatus = result.Status()))
1048 return handleConnectionError("Could not obtain service discovery status");
1049
1050 if (commStatus != GattCommunicationStatus::Success)
1051 return;
1052
1053 auto deviceServices = SAFE(result.Services());
1054 if (!deviceServices)
1055 return handleConnectionError("Could not obtain service list");
1056
1057 uint serviceCount;
1058 if (!TRY(serviceCount = deviceServices.Size()))
1059 return handleConnectionError("Could not obtain service list size");
1060
1061 for (uint i = 0; i < serviceCount; ++i) {
1062
1063 auto deviceService = SAFE(deviceServices.GetAt(i));
1064 if (!deviceService)
1065 WARN_AND_CONTINUE("Could not obtain service");
1066
1067 GUID guuid;
1068 if (!TRY(guuid = deviceService.Uuid()))
1069 WARN_AND_CONTINUE("Could not obtain service's Uuid");
1070
1071 const QBluetoothUuid service(guuid);
1072 m_openedServices.insert(service, deviceService);
1073
1074 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__
1075 << "Changing service pointer from thread"
1076 << QThread::currentThread();
1077 QSharedPointer<QLowEnergyServicePrivate> pointer;
1078 if (serviceList.contains(service)) {
1079 pointer = serviceList.value(service);
1080 } else {
1082 priv->uuid = service;
1083 priv->setController(this);
1084
1085 pointer = QSharedPointer<QLowEnergyServicePrivate>(priv);
1086 serviceList.insert(service, pointer);
1087 }
1088 pointer->type |= QLowEnergyService::PrimaryService;
1089
1090 obtainIncludedServices(pointer, deviceService);
1091 // The obtainIncludedServices method calls await(), so the device can be
1092 // disconnected by the time we return from it. TODO - rewrite in an
1093 // async way!
1094 if (state != QLowEnergyController::DiscoveringState) {
1095 emit q->discoveryFinished(); // Probably not needed when the device
1096 // is already disconnected?
1097 return;
1098 }
1099
1100 emit q->serviceDiscovered(service);
1101 }
1102
1103 setState(QLowEnergyController::DiscoveredState);
1104 emit q->discoveryFinished();
1105}
1106
1107void QLowEnergyControllerPrivateWinRT::clearAllServices()
1108{
1109 // These services will be closed in the respective
1110 // QWinRTLowEnergyServiceHandler workers (in background threads).
1111 for (auto &uuid : m_requestDetailsServiceUuids)
1112 m_openedServices.remove(uuid);
1113 m_requestDetailsServiceUuids.clear();
1114
1115 for (auto service : m_openedServices)
1116 TRY(service.Close());
1117 m_openedServices.clear();
1118}
1119
1120void QLowEnergyControllerPrivateWinRT::closeAndRemoveService(const QBluetoothUuid &uuid)
1121{
1122 auto record = m_openedServices.find(uuid);
1123 if (record != m_openedServices.end()) {
1124 auto service = record.value();
1125 m_openedServices.erase(record);
1126 if (service)
1127 TRY(service.Close());
1128 }
1129}
1130
1131void QLowEnergyControllerPrivateWinRT::discoverServices()
1132{
1133 qCDebug(QT_BT_WINDOWS) << "Service discovery initiated";
1134 // clear the previous services cache, as we request the services again
1135 clearAllServices();
1136
1137 auto asyncResult = SAFE(mDevice.GetGattServicesAsync());
1138 if (!asyncResult)
1139 return handleConnectionError("Could not obtain services");
1140
1141 if (!TRY(asyncResult.Completed({ this, &QLowEnergyControllerPrivateWinRT::onServiceDiscoveryFinished })))
1142 return handleConnectionError("Could not register services discovery callback");
1143}
1144
1145void QLowEnergyControllerPrivateWinRT::discoverServiceDetails(
1146 const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode)
1147{
1148 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << service;
1149 if (!serviceList.contains(service)) {
1150 qCWarning(QT_BT_WINDOWS) << "Discovery done of unknown service:"
1151 << service.toString();
1152 return;
1153 }
1154
1155 // clear the cache to rediscover service details
1156 closeAndRemoveService(service);
1157
1158 auto serviceCallback = [service, mode, this](GattDeviceService deviceService) {
1159 discoverServiceDetailsHelper(service, mode, deviceService);
1160 };
1161
1162 if (!getNativeService(service, serviceCallback))
1163 qCDebug(QT_BT_WINDOWS) << "Could not obtain native service for uuid " << service;
1164}
1165
1166void QLowEnergyControllerPrivateWinRT::discoverServiceDetailsHelper(
1167 const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode,
1168 GattDeviceService deviceService)
1169{
1170 auto reactOnDiscoveryError = [](QSharedPointer<QLowEnergyServicePrivate> service,
1171 const auto &msg)
1172 {
1173 qCDebug(QT_BT_WINDOWS) << msg;
1174 service->setError(QLowEnergyService::UnknownError);
1175 service->setState(QLowEnergyService::RemoteService);
1176 };
1177 //update service data
1178 QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
1179 if (!pointer) {
1180 qCDebug(QT_BT_WINDOWS) << "Device was disconnected while doing service discovery";
1181 return;
1182 }
1183 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1184 << QThread::currentThread();
1185 pointer->setState(QLowEnergyService::RemoteServiceDiscovering);
1186
1187 auto result = SAFE(await_forever(deviceService.GetIncludedServicesAsync()));
1188 if (!result)
1189 return reactOnDiscoveryError(pointer, "Could not obtain included service list");
1190
1191 if (SAFE(result.Status() != GattCommunicationStatus::Success))
1192 return reactOnDiscoveryError(pointer, "Obtaining list of included services failed");
1193
1194 auto deviceServices = SAFE(result.Services());
1195 if (!deviceServices)
1196 return reactOnDiscoveryError(pointer, "Could not obtain service list from result");
1197
1198 uint serviceCount;
1199 if (!TRY(serviceCount = deviceServices.Size()))
1200 return reactOnDiscoveryError(pointer, "Could not obtain included service list's size");
1201
1202 for (uint i = 0; i < serviceCount; ++i) {
1203
1204 auto includedService = SAFE(deviceServices.GetAt(i));
1205 if (!includedService)
1206 WARN_AND_CONTINUE("Could not obtain service from list");
1207
1208 GUID guuid;
1209 if (!TRY(guuid = includedService.Uuid()))
1210 WARN_AND_CONTINUE("Could not obtain service Uuid");
1211
1212 const QBluetoothUuid service(guuid);
1213 if (service.isNull()) {
1214 qCDebug(QT_BT_WINDOWS) << "Could not find service";
1215 continue;
1216 }
1217
1218 pointer->includedServices.append(service);
1219
1220 // update the type of the included service
1221 QSharedPointer<QLowEnergyServicePrivate> otherService = serviceList.value(service);
1222 if (!otherService.isNull())
1223 otherService->type |= QLowEnergyService::IncludedService;
1224 }
1225
1227 new QWinRTLowEnergyServiceHandler(service, deviceService, mode);
1228 m_requestDetailsServiceUuids.insert(service);
1229 QThread *thread = new QThread;
1230 worker->moveToThread(thread);
1231 connect(thread, &QThread::started, worker, &QWinRTLowEnergyServiceHandler::obtainCharList);
1232 connect(thread, &QThread::finished, worker, &QObject::deleteLater);
1233 connect(worker, &QObject::destroyed, thread, &QObject::deleteLater);
1234 connect(this, &QLowEnergyControllerPrivateWinRT::abortConnection,
1235 worker, &QWinRTLowEnergyServiceHandler::setAbortRequested);
1236 connect(worker, &QWinRTLowEnergyServiceHandler::errorOccured,
1237 this, &QLowEnergyControllerPrivateWinRT::handleServiceHandlerError);
1238 connect(worker, &QWinRTLowEnergyServiceHandler::charListObtained, this,
1239 [this](const QBluetoothUuid &service, QHash<QLowEnergyHandle,
1240 QLowEnergyServicePrivate::CharData> charList, QList<QBluetoothUuid> indicateChars,
1241 QLowEnergyHandle startHandle, QLowEnergyHandle endHandle) {
1242 if (!serviceList.contains(service)) {
1243 qCWarning(QT_BT_WINDOWS)
1244 << "Discovery complete for unknown service:" << service.toString();
1245 return;
1246 }
1247 m_requestDetailsServiceUuids.remove(service);
1248
1249 QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
1250 pointer->startHandle = startHandle;
1251 pointer->endHandle = endHandle;
1252 pointer->characteristicList = charList;
1253
1254 for (const QBluetoothUuid &indicateChar : std::as_const(indicateChars))
1255 registerForValueChanges(service, indicateChar);
1256
1257 pointer->setState(QLowEnergyService::RemoteServiceDiscovered);
1258 });
1259 thread->start();
1260}
1261
1262void QLowEnergyControllerPrivateWinRT::startAdvertising(
1263 const QLowEnergyAdvertisingParameters &,
1264 const QLowEnergyAdvertisingData &,
1265 const QLowEnergyAdvertisingData &)
1266{
1267 setError(QLowEnergyController::AdvertisingError);
1268 Q_UNIMPLEMENTED();
1269}
1270
1271void QLowEnergyControllerPrivateWinRT::stopAdvertising()
1272{
1273 Q_UNIMPLEMENTED();
1274}
1275
1276void QLowEnergyControllerPrivateWinRT::requestConnectionUpdate(const QLowEnergyConnectionParameters &)
1277{
1278 Q_UNIMPLEMENTED();
1279}
1280
1281void QLowEnergyControllerPrivateWinRT::readCharacteristic(
1282 const QSharedPointer<QLowEnergyServicePrivate> service,
1283 const QLowEnergyHandle charHandle)
1284{
1285 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << service << charHandle;
1286 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1287 << QThread::currentThread();
1288 Q_ASSERT(!service.isNull());
1289 if (role == QLowEnergyController::PeripheralRole) {
1290 service->setError(QLowEnergyService::CharacteristicReadError);
1291 Q_UNIMPLEMENTED();
1292 return;
1293 }
1294
1295 if (!service->characteristicList.contains(charHandle)) {
1296 qCDebug(QT_BT_WINDOWS) << charHandle << "could not be found in service" << service->uuid;
1297 service->setError(QLowEnergyService::CharacteristicReadError);
1298 return;
1299 }
1300
1301 const auto charData = service->characteristicList.value(charHandle);
1302 if (!(charData.properties & QLowEnergyCharacteristic::Read))
1303 qCDebug(QT_BT_WINDOWS) << "Read flag is not set for characteristic" << charData.uuid;
1304
1305 auto characteristicCallback = [charHandle, service, this](
1306 GattCharacteristic characteristic) {
1307 readCharacteristicHelper(service, charHandle, characteristic);
1308 };
1309
1310 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1311 qCDebug(QT_BT_WINDOWS) << "Could not obtain native characteristic" << charData.uuid
1312 << "from service" << service->uuid;
1313 service->setError(QLowEnergyService::CharacteristicReadError);
1314 }
1315}
1316
1317void QLowEnergyControllerPrivateWinRT::readCharacteristicHelper(
1318 const QSharedPointer<QLowEnergyServicePrivate> service,
1319 const QLowEnergyHandle charHandle,
1320 GattCharacteristic characteristic)
1321{
1322 auto readOp = SAFE(characteristic.ReadValueAsync(BluetoothCacheMode::Uncached));
1323 if (!readOp)
1324 RETURN_SERVICE_ERROR("Could not read characteristic", service, QLowEnergyService::CharacteristicReadError);
1325
1326 auto readCompletedLambda = [charHandle, service]
1327 (IAsyncOperation<GattReadResult> const &op, winrt::AsyncStatus const status)
1328 {
1329 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1330 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charHandle << "read operation failed.";
1331 service->setError(QLowEnergyService::CharacteristicReadError);
1332 return;
1333 }
1334 auto characteristicValue = SAFE(op.GetResults());
1335 if (!characteristicValue)
1336 RETURN_SERVICE_ERROR("Could not obtain result for characteristic", service, QLowEnergyService::CharacteristicReadError);
1337
1338 const QByteArray value = byteArrayFromGattResult(characteristicValue);
1339 auto charData = service->characteristicList.value(charHandle);
1340 charData.value = value;
1341 service->characteristicList.insert(charHandle, charData);
1342 emit service->characteristicRead(QLowEnergyCharacteristic(service, charHandle), value);
1343 return;
1344 };
1345
1346 if (!TRY(readOp.Completed(readCompletedLambda)))
1347 RETURN_SERVICE_ERROR("Could not register characteristic read callback", service, QLowEnergyService::CharacteristicReadError);
1348}
1349
1350void QLowEnergyControllerPrivateWinRT::readDescriptor(
1351 const QSharedPointer<QLowEnergyServicePrivate> service,
1352 const QLowEnergyHandle charHandle,
1353 const QLowEnergyHandle descHandle)
1354{
1355 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << service << charHandle << descHandle;
1356 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1357 << QThread::currentThread();
1358 Q_ASSERT(!service.isNull());
1359 if (role == QLowEnergyController::PeripheralRole) {
1360 service->setError(QLowEnergyService::DescriptorReadError);
1361 Q_UNIMPLEMENTED();
1362 return;
1363 }
1364
1365 if (!service->characteristicList.contains(charHandle)) {
1366 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "in characteristic" << charHandle
1367 << "cannot be found in service" << service->uuid;
1368 service->setError(QLowEnergyService::DescriptorReadError);
1369 return;
1370 }
1371
1372 const auto charData = service->characteristicList.value(charHandle);
1373
1374 auto characteristicCallback = [charHandle, descHandle, service, this](
1375 GattCharacteristic characteristic) {
1376 readDescriptorHelper(service, charHandle, descHandle, characteristic);
1377 };
1378
1379 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1380 qCDebug(QT_BT_WINDOWS) << "Could not obtain native characteristic" << charData.uuid
1381 << "from service" << service->uuid;
1382 service->setError(QLowEnergyService::DescriptorReadError);
1383 }
1384}
1385
1386void QLowEnergyControllerPrivateWinRT::readDescriptorHelper(
1387 const QSharedPointer<QLowEnergyServicePrivate> service,
1388 const QLowEnergyHandle charHandle,
1389 const QLowEnergyHandle descHandle,
1390 GattCharacteristic characteristic)
1391{
1392 // Get native descriptor
1393 const auto charData = service->characteristicList.value(charHandle);
1394 if (!charData.descriptorList.contains(descHandle)) {
1395 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "cannot be found in characteristic"
1396 << charHandle;
1397 }
1398 const auto descData = charData.descriptorList.value(descHandle);
1399 const QBluetoothUuid descUuid = descData.uuid;
1400 if (descUuid ==
1401 QBluetoothUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration)) {
1402
1403 auto readOp = SAFE(characteristic.ReadClientCharacteristicConfigurationDescriptorAsync());
1404 if (!readOp)
1405 RETURN_SERVICE_ERROR("Could not read client characteristic configuration", service, QLowEnergyService::DescriptorReadError);
1406
1407 auto readCompletedLambda = [charHandle, descHandle, service]
1408 (IAsyncOperation<ClientCharConfigDescriptorResult> const &op, winrt::AsyncStatus const status)
1409 {
1410 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1411 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "read operation failed";
1412 service->setError(QLowEnergyService::DescriptorReadError);
1413 return;
1414 }
1415
1416 auto iValue = SAFE(op.GetResults());
1417 if (!iValue)
1418 RETURN_SERVICE_ERROR("Could not obtain result for descriptor", service, QLowEnergyService::DescriptorReadError);
1419 GattClientCharacteristicConfigurationDescriptorValue value;
1420 if (!TRY(value = iValue.ClientCharacteristicConfigurationDescriptor()))
1421 RETURN_SERVICE_ERROR("Could not obtain value for descriptor", service, QLowEnergyService::DescriptorReadError);
1422
1423 quint16 result = 0;
1424 bool correct = false;
1425 if (value & GattClientCharacteristicConfigurationDescriptorValue::Indicate) {
1426 result |= QLowEnergyCharacteristic::Indicate;
1427 correct = true;
1428 }
1429 if (value & GattClientCharacteristicConfigurationDescriptorValue::Notify) {
1430 result |= QLowEnergyCharacteristic::Notify;
1431 correct = true;
1432 }
1433 if (value == GattClientCharacteristicConfigurationDescriptorValue::None)
1434 correct = true;
1435 if (!correct) {
1436 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle
1437 << "read operation failed. Obtained unexpected value.";
1438 service->setError(QLowEnergyService::DescriptorReadError);
1439 return;
1440 }
1442 descData.uuid = QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration;
1443 descData.value = QByteArray(2, Qt::Uninitialized);
1444 qToLittleEndian(result, descData.value.data());
1445 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
1446 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
1447 descData.value);
1448 return;
1449 };
1450
1451 if (!TRY(readOp.Completed(readCompletedLambda)))
1452 RETURN_SERVICE_ERROR("Could not register descriptor read callback", service, QLowEnergyService::DescriptorReadError);
1453
1454 return;
1455 }
1456
1457 auto result = SAFE(await(characteristic.GetDescriptorsForUuidAsync(GUID(descData.uuid))));
1458 if (!result)
1459 RETURN_SERVICE_ERROR("Could not obtain descriptor for uuid", service, QLowEnergyService::DescriptorReadError);
1460
1461 GattCommunicationStatus commStatus;
1462 if (!TRY(commStatus = result.Status()) || commStatus != GattCommunicationStatus::Success) {
1463 qErrnoWarning("Could not obtain list of descriptors");
1464 service->setError(QLowEnergyService::DescriptorReadError);
1465 return;
1466 }
1467
1468 auto descriptors = SAFE(result.Descriptors());
1469 if (!descriptors)
1470 RETURN_SERVICE_ERROR("Could not obtain descriptor list", service, QLowEnergyService::DescriptorReadError);
1471
1472 uint size;
1473 if (!TRY(size = descriptors.Size()))
1474 RETURN_SERVICE_ERROR("Could not obtain descriptor list size", service, QLowEnergyService::DescriptorReadError);
1475
1476 if (size == 0) {
1477 qCWarning(QT_BT_WINDOWS) << "No descriptor with uuid" << descData.uuid << "was found.";
1478 service->setError(QLowEnergyService::DescriptorReadError);
1479 return;
1480 } else if (size > 1) {
1481 qCWarning(QT_BT_WINDOWS) << "There is more than 1 descriptor with uuid" << descData.uuid;
1482 }
1483
1484 auto descriptor = SAFE(descriptors.GetAt(0));
1485 if (!descriptor)
1486 RETURN_SERVICE_ERROR("Could not obtain descriptor from list", service, QLowEnergyService::DescriptorReadError);
1487
1488 auto readOp = SAFE(descriptor.ReadValueAsync(BluetoothCacheMode::Uncached));
1489 if (!readOp)
1490 RETURN_SERVICE_ERROR("Could not read descriptor value", service, QLowEnergyService::DescriptorReadError);
1491
1492 auto readCompletedLambda = [charHandle, descHandle, descUuid, service]
1493 (IAsyncOperation<GattReadResult> const &op, winrt::AsyncStatus const status)
1494 {
1495 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1496 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "read operation failed";
1497 service->setError(QLowEnergyService::DescriptorReadError);
1498 return;
1499 }
1500
1501 auto descriptorValue = SAFE(op.GetResults());
1502 if (!descriptorValue) {
1503 qCDebug(QT_BT_WINDOWS) << "Could not obtain result for descriptor" << descHandle;
1504 service->setError(QLowEnergyService::DescriptorReadError);
1505 return;
1506 }
1508 descData.uuid = descUuid;
1509 if (descData.uuid == QBluetoothUuid::DescriptorType::CharacteristicUserDescription)
1510 descData.value = byteArrayFromGattResult(descriptorValue, true);
1511 else
1512 descData.value = byteArrayFromGattResult(descriptorValue);
1513 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
1514 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
1515 descData.value);
1516 };
1517 if (!TRY(readOp.Completed(readCompletedLambda)))
1518 RETURN_SERVICE_ERROR("Could not register descriptor read callback", service, QLowEnergyService::DescriptorReadError);
1519}
1520
1521void QLowEnergyControllerPrivateWinRT::writeCharacteristic(
1522 const QSharedPointer<QLowEnergyServicePrivate> service,
1523 const QLowEnergyHandle charHandle,
1524 const QByteArray &newValue,
1525 QLowEnergyService::WriteMode mode)
1526{
1527 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << service << charHandle << newValue << mode;
1528 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1529 << QThread::currentThread();
1530 Q_ASSERT(!service.isNull());
1531 if (role == QLowEnergyController::PeripheralRole) {
1532 service->setError(QLowEnergyService::CharacteristicWriteError);
1533 Q_UNIMPLEMENTED();
1534 return;
1535 }
1536 if (!service->characteristicList.contains(charHandle)) {
1537 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charHandle << "cannot be found in service"
1538 << service->uuid;
1539 service->setError(QLowEnergyService::CharacteristicWriteError);
1540 return;
1541 }
1542
1543 QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
1544 const bool writeWithResponse = mode == QLowEnergyService::WriteWithResponse;
1545 if (!(charData.properties & (writeWithResponse ? QLowEnergyCharacteristic::Write
1546 : QLowEnergyCharacteristic::WriteNoResponse)))
1547 qCDebug(QT_BT_WINDOWS) << "Write flag is not set for characteristic" << charHandle;
1548
1549 auto characteristicCallback = [charHandle, service, newValue, writeWithResponse, this](
1550 GattCharacteristic characteristic) {
1551 writeCharacteristicHelper(service, charHandle, newValue, writeWithResponse,
1552 characteristic);
1553 };
1554
1555 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1556 qCDebug(QT_BT_WINDOWS) << "Could not obtain native characteristic" << charData.uuid
1557 << "from service" << service->uuid;
1558 service->setError(QLowEnergyService::CharacteristicWriteError);
1559 }
1560}
1561
1562void QLowEnergyControllerPrivateWinRT::writeCharacteristicHelper(
1563 const QSharedPointer<QLowEnergyServicePrivate> service,
1564 const QLowEnergyHandle charHandle, const QByteArray &newValue,
1565 bool writeWithResponse, GattCharacteristic characteristic)
1566{
1567 const quint32 length = quint32(newValue.length());
1568 Buffer buffer = nullptr;
1569 if (!TRY(buffer = Buffer(length)))
1570 RETURN_SERVICE_ERROR("Could not create buffer", service, QLowEnergyService::CharacteristicWriteError);
1571
1572 byte *bytes;
1573 if (!TRY(bytes = buffer.data()))
1574 RETURN_SERVICE_ERROR("Could not set buffer", service, QLowEnergyService::CharacteristicWriteError);
1575
1576 memcpy(bytes, newValue, length);
1577 if (!TRY(buffer.Length(length)))
1578 RETURN_SERVICE_ERROR("Could not set buffer length", service, QLowEnergyService::CharacteristicWriteError);
1579
1580 GattWriteOption option = writeWithResponse ? GattWriteOption::WriteWithResponse
1581 : GattWriteOption::WriteWithoutResponse;
1582 auto writeOp = SAFE(characteristic.WriteValueAsync(buffer, option));
1583 if (!writeOp)
1584 RETURN_SERVICE_ERROR("Could not write characteristic", service, QLowEnergyService::CharacteristicWriteError);
1585
1586 const auto charData = service->characteristicList.value(charHandle);
1587 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(this);
1588 auto writeCompletedLambda =
1589 [charData, charHandle, newValue, service, writeWithResponse, thisPtr]
1590 (IAsyncOperation<GattCommunicationStatus> const &op, winrt::AsyncStatus const status)
1591 {
1592 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1593 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charHandle
1594 << "write operation failed (async status)";
1595 service->setError(QLowEnergyService::CharacteristicWriteError);
1596 return;
1597 }
1598
1599 GattCommunicationStatus result;
1600 auto hr = HR(result = op.GetResults());
1601 if (hr == E_BLUETOOTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH) {
1602 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charHandle
1603 << "write operation was tried with invalid value length";
1604 service->setError(QLowEnergyService::CharacteristicWriteError);
1605 return;
1606 } else if (FAILED(hr)) {
1607 RETURN_SERVICE_ERROR("Could not obtain characteristic write result", service, QLowEnergyService::CharacteristicWriteError);
1608 }
1609
1610 if (result != GattCommunicationStatus::Success) {
1611 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charHandle
1612 << "write operation failed (communication status)";
1613 service->setError(QLowEnergyService::CharacteristicWriteError);
1614 return;
1615 }
1616 // only update cache when property is readable. Otherwise it remains
1617 // empty.
1618 if (thisPtr && charData.properties & QLowEnergyCharacteristic::Read)
1619 thisPtr->updateValueOfCharacteristic(charHandle, newValue, false);
1620 if (writeWithResponse) {
1621 emit service->characteristicWritten(QLowEnergyCharacteristic(service, charHandle),
1622 newValue);
1623 }
1624 };
1625
1626 if (!TRY(writeOp.Completed(writeCompletedLambda)))
1627 RETURN_SERVICE_ERROR("Could not register characteristic write callback", service, QLowEnergyService::CharacteristicWriteError);
1628}
1629
1630void QLowEnergyControllerPrivateWinRT::writeDescriptor(
1631 const QSharedPointer<QLowEnergyServicePrivate> service,
1632 const QLowEnergyHandle charHandle,
1633 const QLowEnergyHandle descHandle,
1634 const QByteArray &newValue)
1635{
1636 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << service << charHandle << descHandle << newValue;
1637 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1638 << QThread::currentThread();
1639 Q_ASSERT(!service.isNull());
1640 if (role == QLowEnergyController::PeripheralRole) {
1641 service->setError(QLowEnergyService::DescriptorWriteError);
1642 Q_UNIMPLEMENTED();
1643 return;
1644 }
1645
1646 if (!service->characteristicList.contains(charHandle)) {
1647 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "in characteristic" << charHandle
1648 << "could not be found in service" << service->uuid;
1649 service->setError(QLowEnergyService::DescriptorWriteError);
1650 return;
1651 }
1652
1653 const auto charData = service->characteristicList.value(charHandle);
1654
1655 auto characteristicCallback = [descHandle, charHandle, service, newValue, this](
1656 GattCharacteristic characteristic) {
1657 writeDescriptorHelper(service, charHandle, descHandle, newValue, characteristic);
1658 };
1659
1660 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1661 qCDebug(QT_BT_WINDOWS) << "Could not obtain native characteristic" << charData.uuid
1662 << "from service" << service->uuid;
1663 service->setError(QLowEnergyService::DescriptorWriteError);
1664 }
1665}
1666
1667void QLowEnergyControllerPrivateWinRT::writeDescriptorHelper(
1668 const QSharedPointer<QLowEnergyServicePrivate> service,
1669 const QLowEnergyHandle charHandle,
1670 const QLowEnergyHandle descHandle,
1671 const QByteArray &newValue,
1672 GattCharacteristic characteristic)
1673{
1674 // Get native descriptor
1675 const auto charData = service->characteristicList.value(charHandle);
1676 if (!charData.descriptorList.contains(descHandle)) {
1677 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle
1678 << "could not be found in Characteristic" << charHandle;
1679 }
1680
1681 QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle);
1682 if (descData.uuid ==
1683 QBluetoothUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration)) {
1684 GattClientCharacteristicConfigurationDescriptorValue value;
1685 quint16 intValue = qFromLittleEndian<quint16>(newValue);
1686 if ((intValue & GattClientCharacteristicConfigurationDescriptorValue::Indicate)
1687 && (intValue & GattClientCharacteristicConfigurationDescriptorValue::Notify)) {
1688 qCWarning(QT_BT_WINDOWS) << "Setting both Indicate and Notify "
1689 "is not supported on WinRT";
1690 value = GattClientCharacteristicConfigurationDescriptorValue(
1691 (GattClientCharacteristicConfigurationDescriptorValue::Indicate
1692 | GattClientCharacteristicConfigurationDescriptorValue::Notify));
1693 } else if (intValue & GattClientCharacteristicConfigurationDescriptorValue::Indicate) {
1694 value = GattClientCharacteristicConfigurationDescriptorValue::Indicate;
1695 } else if (intValue & GattClientCharacteristicConfigurationDescriptorValue::Notify) {
1696 value = GattClientCharacteristicConfigurationDescriptorValue::Notify;
1697 } else if (intValue == 0) {
1698 value = GattClientCharacteristicConfigurationDescriptorValue::None;
1699 } else {
1700 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle
1701 << "write operation failed: Invalid value";
1702 service->setError(QLowEnergyService::DescriptorWriteError);
1703 return;
1704 }
1705
1706 auto writeOp = SAFE(characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(value));
1707 if (!writeOp)
1708 RETURN_SERVICE_ERROR("Could not write client characteristic configuration", service, QLowEnergyService::DescriptorWriteError);
1709
1710 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(this);
1711 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
1712 (IAsyncOperation<GattCommunicationStatus> const &op, winrt::AsyncStatus const status)
1713 {
1714 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1715 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "write operation failed";
1716 service->setError(QLowEnergyService::DescriptorWriteError);
1717 return;
1718 }
1719
1720 GattCommunicationStatus result;
1721 if (!TRY(result = op.GetResults()))
1722 RETURN_SERVICE_ERROR("Could not obtain result for descriptor", service, QLowEnergyService::DescriptorWriteError);
1723
1724 if (result != GattCommunicationStatus::Success) {
1725 qCWarning(QT_BT_WINDOWS) << "Descriptor" << descHandle << "write operation failed";
1726 service->setError(QLowEnergyService::DescriptorWriteError);
1727 return;
1728 }
1729 if (thisPtr)
1730 thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue, false);
1731 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
1732 newValue);
1733 };
1734
1735 if (!TRY(writeOp.Completed(writeCompletedLambda)))
1736 RETURN_SERVICE_ERROR("Could not register descriptor write callback", service, QLowEnergyService::DescriptorWriteError);
1737
1738 return;
1739 }
1740
1741 auto result = SAFE(await(characteristic.GetDescriptorsForUuidAsync(GUID(descData.uuid))));
1742 if (!result)
1743 RETURN_SERVICE_ERROR("Could not obtain descriptor from Uuid", service, QLowEnergyService::DescriptorWriteError);
1744
1745 GattCommunicationStatus commStatus;
1746 if (!TRY(commStatus = result.Status()) || commStatus != GattCommunicationStatus::Success) {
1747 qCWarning(QT_BT_WINDOWS) << "Descriptor operation failed";
1748 service->setError(QLowEnergyService::DescriptorWriteError);
1749 return;
1750 }
1751
1752 auto descriptors = SAFE(result.Descriptors());
1753 if (!descriptors)
1754 RETURN_SERVICE_ERROR("Could not obtain list of descriptors", service, QLowEnergyService::DescriptorWriteError);
1755
1756 uint size;
1757 if (!TRY(size = descriptors.Size()))
1758 RETURN_SERVICE_ERROR("Could not obtain list of descriptors' size", service, QLowEnergyService::DescriptorWriteError);
1759
1760 if (size == 0) {
1761 qCWarning(QT_BT_WINDOWS) << "No descriptor with uuid" << descData.uuid << "was found.";
1762 return;
1763 } else if (size > 1) {
1764 qCWarning(QT_BT_WINDOWS) << "There is more than 1 descriptor with uuid" << descData.uuid;
1765 }
1766
1767 auto descriptor = SAFE(descriptors.GetAt(0));
1768 if (!descriptor)
1769 RETURN_SERVICE_ERROR("Could not obtain descriptor", service, QLowEnergyService::DescriptorWriteError);
1770
1771 const quint32 length = quint32(newValue.length());
1772 Buffer buffer = nullptr;
1773 if (!TRY(buffer = Buffer(length)))
1774 RETURN_SERVICE_ERROR("Could not create buffer", service, QLowEnergyService::CharacteristicWriteError);
1775
1776 byte *bytes;
1777 if (!TRY(bytes = buffer.data()))
1778 RETURN_SERVICE_ERROR("Could not set buffer", service, QLowEnergyService::CharacteristicWriteError);
1779
1780 memcpy(bytes, newValue, length);
1781 if (!TRY(buffer.Length(length)))
1782 RETURN_SERVICE_ERROR("Could not set buffer length", service, QLowEnergyService::CharacteristicWriteError);
1783
1784 auto writeOp = SAFE(descriptor.WriteValueAsync(buffer));
1785 if (!writeOp)
1786 RETURN_SERVICE_ERROR("Could not write descriptor value", service, QLowEnergyService::CharacteristicWriteError);
1787
1788 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(this);
1789 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
1790 (IAsyncOperation<GattCommunicationStatus> const &op, winrt::AsyncStatus const status)
1791 {
1792 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1793 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "write operation failed";
1794 service->setError(QLowEnergyService::DescriptorWriteError);
1795 return;
1796 }
1797
1798 GattCommunicationStatus result;
1799 if (!TRY(result = op.GetResults()))
1800 RETURN_SERVICE_ERROR("Could not obtain result for descriptor", service, QLowEnergyService::DescriptorWriteError);
1801
1802 if (result != GattCommunicationStatus::Success) {
1803 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "write operation failed";
1804 service->setError(QLowEnergyService::DescriptorWriteError);
1805 return;
1806 }
1807 if (thisPtr)
1808 thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue, false);
1809 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
1810 newValue);
1811 };
1812
1813 if (!TRY(writeOp.Completed(writeCompletedLambda)))
1814 RETURN_SERVICE_ERROR("Could not register descriptor write callback", service, QLowEnergyService::DescriptorWriteError);
1815
1816}
1817
1818void QLowEnergyControllerPrivateWinRT::addToGenericAttributeList(const QLowEnergyServiceData &,
1820{
1821 Q_UNIMPLEMENTED();
1822}
1823
1824int QLowEnergyControllerPrivateWinRT::mtu() const
1825{
1826 uint16_t mtu = 23;
1827 if (!mGattSession) {
1828 qCDebug(QT_BT_WINDOWS) << "mtu queried before GattSession available. Using default mtu.";
1829 return mtu;
1830 }
1831
1832 if (!TRY(mtu = mGattSession.MaxPduSize()))
1833 RETURN_FALSE("could not obtain MTU size");
1834
1835 qCDebug(QT_BT_WINDOWS) << "mtu determined to be" << mtu;
1836 return mtu;
1837}
1838
1839void QLowEnergyControllerPrivateWinRT::handleCharacteristicChanged(
1840 quint16 charHandle, const QByteArray &data)
1841{
1842 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << charHandle << data;
1843 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1844 << QThread::currentThread();
1845 QSharedPointer<QLowEnergyServicePrivate> service =
1846 serviceForHandle(charHandle);
1847 if (service.isNull())
1848 return;
1849
1850 qCDebug(QT_BT_WINDOWS) << "Characteristic change notification" << service->uuid
1851 << charHandle << data.toHex();
1852
1853 QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle);
1854 if (!characteristic.isValid()) {
1855 qCWarning(QT_BT_WINDOWS) << "characteristicChanged: Cannot find characteristic";
1856 return;
1857 }
1858
1859 // only update cache when property is readable. Otherwise it remains
1860 // empty.
1861 if (characteristic.properties() & QLowEnergyCharacteristic::Read)
1862 updateValueOfCharacteristic(characteristic.attributeHandle(),
1863 data, false);
1864 emit service->characteristicChanged(characteristic, data);
1865}
1866
1867void QLowEnergyControllerPrivateWinRT::handleServiceHandlerError(const QString &error)
1868{
1869 if (state != QLowEnergyController::DiscoveringState)
1870 return;
1871
1872 qCWarning(QT_BT_WINDOWS) << "Error while discovering services:" << error;
1873 setState(QLowEnergyController::UnconnectedState);
1874 setError(QLowEnergyController::ConnectionError);
1875}
1876
1877void QLowEnergyControllerPrivateWinRT::handleConnectionError(const char *logMessage)
1878{
1879 qCWarning(QT_BT_WINDOWS) << logMessage;
1880 setError(QLowEnergyController::ConnectionError);
1881 setState(QLowEnergyController::UnconnectedState);
1882 unregisterFromStatusChanges();
1883 unregisterFromMtuChanges();
1884}
1885
1886QT_END_NAMESPACE
1887
1888#include "qlowenergycontroller_winrt.moc"
void discoverServiceDetails(const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode) override
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
void setController(QLowEnergyControllerPrivate *control)
void errorOccurred(const QString &error)
QHash< QLowEnergyHandle, QLowEnergyServicePrivate::CharData > mCharacteristicList
void errorOccured(const QString &error)
QLowEnergyService::DiscoveryMode mMode
#define SAFE(x)
#define HR(x)
#define TRY(x)
void registerQLowEnergyControllerMetaType()
#define RETURN_FALSE(msg)
#define DEC_CHAR_COUNT_AND_CONTINUE(msg)
constexpr T & bitwise_or_equal(T &a, const E &b)
#define ENUM_BITWISE_OPS(E)
QT_BEGIN_NAMESPACE typedef GattReadClientCharacteristicConfigurationDescriptorResult ClientCharConfigDescriptorResult
static T await_forever(IAsyncOperation< T > asyncInfo, GlobalCondition canceled=never)
static QByteArray byteArrayFromGattResult(GattReadResult gattResult, bool isWCharString=false)
constexpr std::underlying_type_t< E > bitwise_or(E x, E y)
constexpr T bitwise_or(T x, E y)
std::function< bool()> GlobalCondition
static T await(IAsyncOperation< T > asyncInfo, GlobalCondition canceled=never, int timeout=5000)
static QByteArray byteArrayFromBuffer(IBuffer buffer, bool isWCharString=false)
#define RETURN_SERVICE_ERROR(msg, service, error)
constexpr std::underlying_type_t< E > bitwise_and(E x, E y)
static constexpr bool never()
constexpr T bitwise_and(T x, E y)
constexpr int timeout_infinity
#define WARN_AND_CONTINUE(msg)
#define RETURN_MSG(msg)