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