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