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 // Update the cache and execute the callback in the main thread
764 QMetaObject::invokeMethod(thisPtr.get(),
765 [thisPtr, callback, service, serviceUuid]() {
766 thisPtr->m_openedServices.insert(serviceUuid, service);
767 callback(service); // Use the service in a custom callback
768 });
769 }
770 } else {
771 qCWarning(QT_BT_WINDOWS) << "No services found for Uuid" << serviceUuid;
772 }
773 }));
774
775 return ok;
776}
777
778bool QLowEnergyControllerPrivateWinRT::getNativeCharacteristic(
779 const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid,
780 NativeCharacteristicCallback callback)
781{
782 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(this);
783 auto serviceCallback = [thisPtr, callback, charUuid](GattDeviceService service) {
784
785 auto characteristicRequestOp = SAFE(service.GetCharacteristicsForUuidAsync(GUID(charUuid)));
786 if (!characteristicRequestOp)
787 RETURN_MSG("Could not start async characteristics request");
788
789 TRY(characteristicRequestOp.Completed(
790 [thisPtr, callback](
791 IAsyncOperation<GattCharacteristicsResult> const &op, winrt::AsyncStatus const status)
792 {
793 if (!thisPtr)
794 return;
795
796 if (status != winrt::AsyncStatus::Completed) {
797 qCDebug(QT_BT_WINDOWS) << "Failed to get result of async characteristic "
798 "operation";
799 return;
800 }
801
802 auto result = SAFE(op.GetResults());
803 if (!result)
804 RETURN_MSG("Failed to get result of async characteristic operation");
805
806 GattCommunicationStatus commStatus;
807 if (!TRY(commStatus = result.Status()) || commStatus != GattCommunicationStatus::Success) {
808 qErrnoWarning("Native characteristic operation failed.");
809 return;
810 }
811
812 auto characteristics = SAFE(result.Characteristics());
813 if (!characteristics)
814 RETURN_MSG("Could not obtain characteristic list.");
815
816 uint size;
817 if (!TRY(size = characteristics.Size()))
818 RETURN_MSG("Could not obtain characteristic list's size.");
819
820 if (size != 1)
821 qErrnoWarning("More than 1 characteristic found.");
822
823 auto characteristic = SAFE(characteristics.GetAt(0));
824 if (!characteristic)
825 RETURN_MSG("Could not obtain first characteristic for service");
826
827 callback(characteristic); // use the characteristic in a custom callback
828 }));
829 };
830
831 if (!getNativeService(serviceUuid, serviceCallback)) {
832 qCDebug(QT_BT_WINDOWS) << "Failed to get native service for" << serviceUuid;
833 return false;
834 }
835
836 return true;
837}
838
839void QLowEnergyControllerPrivateWinRT::registerForValueChanges(const QBluetoothUuid &serviceUuid,
840 const QBluetoothUuid &charUuid)
841{
842 qCDebug(QT_BT_WINDOWS) << "Registering characteristic" << charUuid << "in service"
843 << serviceUuid << "for value changes";
844 for (const ValueChangedEntry &entry : std::as_const(mValueChangedTokens)) {
845 GUID guuid{ 0 };
846 if (!TRY(guuid = entry.characteristic.Uuid()))
847 WARN_AND_CONTINUE("Could not obtain characteristic's Uuid");
848 if (QBluetoothUuid(guuid) == charUuid)
849 return;
850 }
851
852 auto callback = [this, charUuid, serviceUuid](GattCharacteristic characteristic) {
853 winrt::event_token token;
854 if (!TRY(token = characteristic.ValueChanged({ this, &QLowEnergyControllerPrivateWinRT::onValueChange })))
855 RETURN_MSG("Could not register characteristic for value changes");
856
857 mValueChangedTokens.append(ValueChangedEntry(characteristic, token));
858 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charUuid << "in service"
859 << serviceUuid << "registered for value changes";
860 };
861
862 if (!getNativeCharacteristic(serviceUuid, charUuid, callback)) {
863 qCDebug(QT_BT_WINDOWS).nospace() << "Could not obtain native characteristic "
864 << charUuid << " from service " << serviceUuid
865 << ". Qt will not be able to signal"
866 << " changes for this characteristic.";
867 }
868}
869
870void QLowEnergyControllerPrivateWinRT::unregisterFromValueChanges()
871{
872 qCDebug(QT_BT_WINDOWS) << "Unregistering " << mValueChangedTokens.size() << " value change tokens";
873 for (const ValueChangedEntry &entry : std::as_const(mValueChangedTokens)) {
874 if (!entry.characteristic) {
875 qCWarning(QT_BT_WINDOWS) << "Unregistering from value changes for characteristic failed."
876 << "Characteristic has been deleted";
877 continue;
878 }
879 if (!TRY(entry.characteristic.ValueChanged(entry.token)))
880 qCWarning(QT_BT_WINDOWS) << "Unregistering from value changes for characteristic failed.";
881 }
882 mValueChangedTokens.clear();
883}
884
885void QLowEnergyControllerPrivateWinRT::onValueChange(GattCharacteristic characteristic, GattValueChangedEventArgs const &args)
886{
887 quint16 handle = 0;
888 if (!TRY(handle = characteristic.AttributeHandle()))
889 RETURN_MSG("Could not obtain characteristic's handle");
890
891 auto buffer = SAFE(args.CharacteristicValue());
892 if (!buffer)
893 RETURN_MSG("Could not obtain characteristic's value");
894
895 emit characteristicChanged(handle, byteArrayFromBuffer(buffer));
896}
897
898bool QLowEnergyControllerPrivateWinRT::registerForMtuChanges()
899{
900 if (!mDevice || !mGattSession)
901 return false;
902 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
903 if (!TRY(mMtuChangedToken = mGattSession.MaxPduSizeChanged({ this, &QLowEnergyControllerPrivateWinRT::onMtuChange })))
904 RETURN_FALSE("Could not add MTU callback");
905 return true;
906}
907
908void QLowEnergyControllerPrivateWinRT::unregisterFromMtuChanges()
909{
910 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
911 if (mDevice && mGattSession && mMtuChangedToken) {
912 TRY(mGattSession.MaxPduSizeChanged(mMtuChangedToken));
913 mMtuChangedToken = { 0 };
914 }
915}
916
917void QLowEnergyControllerPrivateWinRT::onMtuChange(GattSession session, winrt::IInspectable args)
918{
919 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
920
921 if (session != mGattSession) {
922 qCWarning(QT_BT_WINDOWS) << "Got MTU changed event for wrong or outdated GattSession.";
923 } else {
924 Q_Q(QLowEnergyController);
925 emit q->mtuChanged(mtu());
926 }
927}
928
929bool QLowEnergyControllerPrivateWinRT::registerForStatusChanges()
930{
931 if (!mDevice)
932 return false;
933
934 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
935
936 if (!TRY(mStatusChangedToken = mDevice.ConnectionStatusChanged({ this, &QLowEnergyControllerPrivateWinRT::onStatusChange })))
937 RETURN_FALSE("Could not add status callback");
938 return true;
939}
940
941void QLowEnergyControllerPrivateWinRT::unregisterFromStatusChanges()
942{
943 qCDebug(QT_BT_WINDOWS) << __FUNCTION__;
944 if (mDevice && mStatusChangedToken) {
945 TRY(mDevice.ConnectionStatusChanged(mStatusChangedToken));
946 mStatusChangedToken = { 0 };
947 }
948}
949
950void QLowEnergyControllerPrivateWinRT::onStatusChange(BluetoothLEDevice dev, winrt::IInspectable args)
951{
952 Q_Q(QLowEnergyController);
953
954 BluetoothConnectionStatus status;
955 if (!TRY(status = dev.ConnectionStatus()))
956 RETURN_MSG("Could not obtain connection status");
957
958 if (state == QLowEnergyController::ConnectingState
959 && status == BluetoothConnectionStatus::Connected) {
960 setState(QLowEnergyController::ConnectedState);
961 emit q->connected();
962 } else if (state != QLowEnergyController::UnconnectedState
963 && status == BluetoothConnectionStatus::Disconnected) {
965 unregisterFromValueChanges();
966 unregisterFromStatusChanges();
967 unregisterFromMtuChanges();
968 clearAllServices();
969 mGattSession = nullptr;
970 mDevice = nullptr;
971 setError(QLowEnergyController::RemoteHostClosedError);
972 setState(QLowEnergyController::UnconnectedState);
973 emit q->disconnected();
974 }
975}
976
977void QLowEnergyControllerPrivateWinRT::obtainIncludedServices(QSharedPointer<QLowEnergyServicePrivate> servicePointer, GattDeviceService service)
978{
979 Q_Q(QLowEnergyController);
980
981 auto result = SAFE(await(service.GetIncludedServicesAsync()));
982 if (!result)
983 RETURN_MSG("Could not obtain included services");
984
985 // The device can be disconnected by the time we return from await()
986 if (state != QLowEnergyController::DiscoveringState)
987 return;
988
989 GattCommunicationStatus status;
990 if (!TRY(status = result.Status())) {
991 qErrnoWarning("Could not obtain list of included services");
992 return;
993 }
994
995 auto includedServices = SAFE(result.Services());
996 if (!includedServices)
997 RETURN_MSG("Could not obtain service list");
998
999 uint count;
1000 if (!TRY(count = includedServices.Size()))
1001 RETURN_MSG("Could not obtain service list's size");
1002
1003 for (uint i = 0; i < count; ++i) {
1004
1005 auto includedService = SAFE(includedServices.GetAt(i));
1006 if (!includedService)
1007 WARN_AND_CONTINUE("Could not obtain service from list");
1008
1009 GUID guuid;
1010 if (!TRY(guuid = includedService.Uuid()))
1011 WARN_AND_CONTINUE("Could not obtain included service's Uuid");
1012
1013 const QBluetoothUuid includedUuid(guuid);
1014 QSharedPointer<QLowEnergyServicePrivate> includedPointer;
1015 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__
1016 << "Changing service pointer from thread"
1017 << QThread::currentThread();
1018 if (serviceList.contains(includedUuid)) {
1019 includedPointer = serviceList.value(includedUuid);
1020 } else {
1022 priv->uuid = includedUuid;
1023 priv->setController(this);
1024
1025 includedPointer = QSharedPointer<QLowEnergyServicePrivate>(priv);
1026 serviceList.insert(includedUuid, includedPointer);
1027 }
1028 includedPointer->type |= QLowEnergyService::IncludedService;
1029 servicePointer->includedServices.append(includedUuid);
1030
1031 obtainIncludedServices(includedPointer, includedService);
1032
1033 emit q->serviceDiscovered(includedUuid);
1034 }
1035}
1036
1037void QLowEnergyControllerPrivateWinRT::onServiceDiscoveryFinished(IAsyncOperation<GattDeviceServicesResult> const &op, winrt::AsyncStatus status)
1038{
1039 // Check if the device is in the proper state, because it can already be
1040 // disconnected when the callback arrives.
1041 // Also the callback can theoretically come when the connection is
1042 // reestablisheed again (for example, if the user quickly clicks
1043 // "Disconnect" and then "Connect" again in some UI). But we can probably
1044 // omit such details, as we are connecting to the same device anyway.
1045 if (state != QLowEnergyController::DiscoveringState)
1046 return;
1047
1048 Q_Q(QLowEnergyController);
1049 if (status != winrt::AsyncStatus::Completed) {
1050 qCDebug(QT_BT_WINDOWS) << "Could not obtain services";
1051 return;
1052 }
1053
1054 auto result = SAFE(op.GetResults());
1055 if (!result)
1056 return handleConnectionError("Could not obtain service discovery result");
1057
1058 GattCommunicationStatus commStatus;
1059 if (!TRY(commStatus = result.Status()))
1060 return handleConnectionError("Could not obtain service discovery status");
1061
1062 if (commStatus != GattCommunicationStatus::Success)
1063 return;
1064
1065 auto deviceServices = SAFE(result.Services());
1066 if (!deviceServices)
1067 return handleConnectionError("Could not obtain service list");
1068
1069 uint serviceCount;
1070 if (!TRY(serviceCount = deviceServices.Size()))
1071 return handleConnectionError("Could not obtain service list size");
1072
1073 for (uint i = 0; i < serviceCount; ++i) {
1074
1075 auto deviceService = SAFE(deviceServices.GetAt(i));
1076 if (!deviceService)
1077 WARN_AND_CONTINUE("Could not obtain service");
1078
1079 GUID guuid;
1080 if (!TRY(guuid = deviceService.Uuid()))
1081 WARN_AND_CONTINUE("Could not obtain service's Uuid");
1082
1083 const QBluetoothUuid service(guuid);
1084 m_openedServices.insert(service, deviceService);
1085
1086 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__
1087 << "Changing service pointer from thread"
1088 << QThread::currentThread();
1089 QSharedPointer<QLowEnergyServicePrivate> pointer;
1090 if (serviceList.contains(service)) {
1091 pointer = serviceList.value(service);
1092 } else {
1094 priv->uuid = service;
1095 priv->setController(this);
1096
1097 pointer = QSharedPointer<QLowEnergyServicePrivate>(priv);
1098 serviceList.insert(service, pointer);
1099 }
1100 pointer->type |= QLowEnergyService::PrimaryService;
1101
1102 obtainIncludedServices(pointer, deviceService);
1103 // The obtainIncludedServices method calls await(), so the device can be
1104 // disconnected by the time we return from it. TODO - rewrite in an
1105 // async way!
1106 if (state != QLowEnergyController::DiscoveringState) {
1107 emit q->discoveryFinished(); // Probably not needed when the device
1108 // is already disconnected?
1109 return;
1110 }
1111
1112 emit q->serviceDiscovered(service);
1113 }
1114
1115 setState(QLowEnergyController::DiscoveredState);
1116 emit q->discoveryFinished();
1117}
1118
1119void QLowEnergyControllerPrivateWinRT::clearAllServices()
1120{
1121 // These services will be closed in the respective
1122 // QWinRTLowEnergyServiceHandler workers (in background threads).
1123 for (auto &uuid : m_requestDetailsServiceUuids)
1124 m_openedServices.remove(uuid);
1125 m_requestDetailsServiceUuids.clear();
1126
1127 for (auto service : m_openedServices)
1128 TRY(service.Close());
1129 m_openedServices.clear();
1130}
1131
1132void QLowEnergyControllerPrivateWinRT::closeAndRemoveService(const QBluetoothUuid &uuid)
1133{
1134 auto record = m_openedServices.find(uuid);
1135 if (record != m_openedServices.end()) {
1136 auto service = record.value();
1137 m_openedServices.erase(record);
1138 if (service)
1139 TRY(service.Close());
1140 }
1141}
1142
1143void QLowEnergyControllerPrivateWinRT::discoverServices()
1144{
1145 qCDebug(QT_BT_WINDOWS) << "Service discovery initiated";
1146 // clear the previous services cache, as we request the services again
1147 clearAllServices();
1148
1149 auto asyncResult = SAFE(mDevice.GetGattServicesAsync());
1150 if (!asyncResult)
1151 return handleConnectionError("Could not obtain services");
1152
1153 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(this);
1154 auto discoveryCallback = [thisPtr](IAsyncOperation<GattDeviceServicesResult> const &op,
1155 winrt::AsyncStatus status) {
1156 if (!thisPtr)
1157 return;
1158 QMetaObject::invokeMethod(thisPtr.get(), [thisPtr, op, status]() {
1159 thisPtr->onServiceDiscoveryFinished(op, status);
1160 });
1161 };
1162
1163 if (!TRY(asyncResult.Completed(discoveryCallback)))
1164 return handleConnectionError("Could not register services discovery callback");
1165}
1166
1167void QLowEnergyControllerPrivateWinRT::discoverServiceDetails(
1168 const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode)
1169{
1170 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << service;
1171 if (!serviceList.contains(service)) {
1172 qCWarning(QT_BT_WINDOWS) << "Discovery done of unknown service:"
1173 << service.toString();
1174 return;
1175 }
1176
1177 // clear the cache to rediscover service details
1178 closeAndRemoveService(service);
1179
1180 auto serviceCallback = [service, mode, this](GattDeviceService deviceService) {
1181 discoverServiceDetailsHelper(service, mode, deviceService);
1182 };
1183
1184 if (!getNativeService(service, serviceCallback))
1185 qCDebug(QT_BT_WINDOWS) << "Could not obtain native service for uuid " << service;
1186}
1187
1188void QLowEnergyControllerPrivateWinRT::discoverServiceDetailsHelper(
1189 const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode,
1190 GattDeviceService deviceService)
1191{
1192 auto reactOnDiscoveryError = [](QSharedPointer<QLowEnergyServicePrivate> service,
1193 const auto &msg)
1194 {
1195 qCDebug(QT_BT_WINDOWS) << msg;
1196 service->setError(QLowEnergyService::UnknownError);
1197 service->setState(QLowEnergyService::RemoteService);
1198 };
1199 //update service data
1200 QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
1201 if (!pointer) {
1202 qCDebug(QT_BT_WINDOWS) << "Device was disconnected while doing service discovery";
1203 return;
1204 }
1205 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1206 << QThread::currentThread();
1207 pointer->setState(QLowEnergyService::RemoteServiceDiscovering);
1208
1209 auto result = SAFE(await_forever(deviceService.GetIncludedServicesAsync()));
1210 if (!result)
1211 return reactOnDiscoveryError(pointer, "Could not obtain included service list");
1212
1213 if (SAFE(result.Status() != GattCommunicationStatus::Success))
1214 return reactOnDiscoveryError(pointer, "Obtaining list of included services failed");
1215
1216 auto deviceServices = SAFE(result.Services());
1217 if (!deviceServices)
1218 return reactOnDiscoveryError(pointer, "Could not obtain service list from result");
1219
1220 uint serviceCount;
1221 if (!TRY(serviceCount = deviceServices.Size()))
1222 return reactOnDiscoveryError(pointer, "Could not obtain included service list's size");
1223
1224 for (uint i = 0; i < serviceCount; ++i) {
1225
1226 auto includedService = SAFE(deviceServices.GetAt(i));
1227 if (!includedService)
1228 WARN_AND_CONTINUE("Could not obtain service from list");
1229
1230 GUID guuid;
1231 if (!TRY(guuid = includedService.Uuid()))
1232 WARN_AND_CONTINUE("Could not obtain service Uuid");
1233
1234 const QBluetoothUuid service(guuid);
1235 if (service.isNull()) {
1236 qCDebug(QT_BT_WINDOWS) << "Could not find service";
1237 continue;
1238 }
1239
1240 pointer->includedServices.append(service);
1241
1242 // update the type of the included service
1243 QSharedPointer<QLowEnergyServicePrivate> otherService = serviceList.value(service);
1244 if (!otherService.isNull())
1245 otherService->type |= QLowEnergyService::IncludedService;
1246 }
1247
1249 new QWinRTLowEnergyServiceHandler(service, deviceService, mode);
1250 m_requestDetailsServiceUuids.insert(service);
1251 QThread *thread = new QThread;
1252 worker->moveToThread(thread);
1253 connect(thread, &QThread::started, worker, &QWinRTLowEnergyServiceHandler::obtainCharList);
1254 connect(thread, &QThread::finished, worker, &QObject::deleteLater);
1255 connect(worker, &QObject::destroyed, thread, &QObject::deleteLater);
1256 connect(this, &QLowEnergyControllerPrivateWinRT::abortConnection,
1257 worker, &QWinRTLowEnergyServiceHandler::setAbortRequested);
1258 connect(worker, &QWinRTLowEnergyServiceHandler::errorOccured,
1259 this, &QLowEnergyControllerPrivateWinRT::handleServiceHandlerError);
1260 connect(worker, &QWinRTLowEnergyServiceHandler::charListObtained, this,
1261 [this](const QBluetoothUuid &service, QHash<QLowEnergyHandle,
1262 QLowEnergyServicePrivate::CharData> charList, QList<QBluetoothUuid> indicateChars,
1263 QLowEnergyHandle startHandle, QLowEnergyHandle endHandle) {
1264 if (!serviceList.contains(service)) {
1265 qCWarning(QT_BT_WINDOWS)
1266 << "Discovery complete for unknown service:" << service.toString();
1267 return;
1268 }
1269 m_requestDetailsServiceUuids.remove(service);
1270
1271 QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
1272 pointer->startHandle = startHandle;
1273 pointer->endHandle = endHandle;
1274 pointer->characteristicList = charList;
1275
1276 for (const QBluetoothUuid &indicateChar : std::as_const(indicateChars))
1277 registerForValueChanges(service, indicateChar);
1278
1279 pointer->setState(QLowEnergyService::RemoteServiceDiscovered);
1280 });
1281 thread->start();
1282}
1283
1284void QLowEnergyControllerPrivateWinRT::startAdvertising(
1285 const QLowEnergyAdvertisingParameters &,
1286 const QLowEnergyAdvertisingData &,
1287 const QLowEnergyAdvertisingData &)
1288{
1289 setError(QLowEnergyController::AdvertisingError);
1290 Q_UNIMPLEMENTED();
1291}
1292
1293void QLowEnergyControllerPrivateWinRT::stopAdvertising()
1294{
1295 Q_UNIMPLEMENTED();
1296}
1297
1298void QLowEnergyControllerPrivateWinRT::requestConnectionUpdate(const QLowEnergyConnectionParameters &)
1299{
1300 Q_UNIMPLEMENTED();
1301}
1302
1303void QLowEnergyControllerPrivateWinRT::readCharacteristic(
1304 const QSharedPointer<QLowEnergyServicePrivate> service,
1305 const QLowEnergyHandle charHandle)
1306{
1307 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << service << charHandle;
1308 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1309 << QThread::currentThread();
1310 Q_ASSERT(!service.isNull());
1311 if (role == QLowEnergyController::PeripheralRole) {
1312 service->setError(QLowEnergyService::CharacteristicReadError);
1313 Q_UNIMPLEMENTED();
1314 return;
1315 }
1316
1317 if (!service->characteristicList.contains(charHandle)) {
1318 qCDebug(QT_BT_WINDOWS) << charHandle << "could not be found in service" << service->uuid;
1319 service->setError(QLowEnergyService::CharacteristicReadError);
1320 return;
1321 }
1322
1323 const auto charData = service->characteristicList.value(charHandle);
1324 if (!(charData.properties & QLowEnergyCharacteristic::Read))
1325 qCDebug(QT_BT_WINDOWS) << "Read flag is not set for characteristic" << charData.uuid;
1326
1327 auto characteristicCallback = [charHandle, service, this](
1328 GattCharacteristic characteristic) {
1329 readCharacteristicHelper(service, charHandle, characteristic);
1330 };
1331
1332 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1333 qCDebug(QT_BT_WINDOWS) << "Could not obtain native characteristic" << charData.uuid
1334 << "from service" << service->uuid;
1335 service->setError(QLowEnergyService::CharacteristicReadError);
1336 }
1337}
1338
1339void QLowEnergyControllerPrivateWinRT::readCharacteristicHelper(
1340 const QSharedPointer<QLowEnergyServicePrivate> service,
1341 const QLowEnergyHandle charHandle,
1342 GattCharacteristic characteristic)
1343{
1344 auto readOp = SAFE(characteristic.ReadValueAsync(BluetoothCacheMode::Uncached));
1345 if (!readOp)
1346 RETURN_SERVICE_ERROR("Could not read characteristic", service, QLowEnergyService::CharacteristicReadError);
1347
1348 auto readCompletedLambda = [charHandle, service]
1349 (IAsyncOperation<GattReadResult> const &op, winrt::AsyncStatus const status)
1350 {
1351 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1352 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charHandle << "read operation failed.";
1353 service->setError(QLowEnergyService::CharacteristicReadError);
1354 return;
1355 }
1356 auto characteristicValue = SAFE(op.GetResults());
1357 if (!characteristicValue)
1358 RETURN_SERVICE_ERROR("Could not obtain result for characteristic", service, QLowEnergyService::CharacteristicReadError);
1359
1360 const QByteArray value = byteArrayFromGattResult(characteristicValue);
1361 auto charData = service->characteristicList.value(charHandle);
1362 charData.value = value;
1363 service->characteristicList.insert(charHandle, charData);
1364 emit service->characteristicRead(QLowEnergyCharacteristic(service, charHandle), value);
1365 return;
1366 };
1367
1368 if (!TRY(readOp.Completed(readCompletedLambda)))
1369 RETURN_SERVICE_ERROR("Could not register characteristic read callback", service, QLowEnergyService::CharacteristicReadError);
1370}
1371
1372void QLowEnergyControllerPrivateWinRT::readDescriptor(
1373 const QSharedPointer<QLowEnergyServicePrivate> service,
1374 const QLowEnergyHandle charHandle,
1375 const QLowEnergyHandle descHandle)
1376{
1377 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << service << charHandle << descHandle;
1378 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1379 << QThread::currentThread();
1380 Q_ASSERT(!service.isNull());
1381 if (role == QLowEnergyController::PeripheralRole) {
1382 service->setError(QLowEnergyService::DescriptorReadError);
1383 Q_UNIMPLEMENTED();
1384 return;
1385 }
1386
1387 if (!service->characteristicList.contains(charHandle)) {
1388 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "in characteristic" << charHandle
1389 << "cannot be found in service" << service->uuid;
1390 service->setError(QLowEnergyService::DescriptorReadError);
1391 return;
1392 }
1393
1394 const auto charData = service->characteristicList.value(charHandle);
1395
1396 auto characteristicCallback = [charHandle, descHandle, service, this](
1397 GattCharacteristic characteristic) {
1398 readDescriptorHelper(service, charHandle, descHandle, characteristic);
1399 };
1400
1401 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1402 qCDebug(QT_BT_WINDOWS) << "Could not obtain native characteristic" << charData.uuid
1403 << "from service" << service->uuid;
1404 service->setError(QLowEnergyService::DescriptorReadError);
1405 }
1406}
1407
1408void QLowEnergyControllerPrivateWinRT::readDescriptorHelper(
1409 const QSharedPointer<QLowEnergyServicePrivate> service,
1410 const QLowEnergyHandle charHandle,
1411 const QLowEnergyHandle descHandle,
1412 GattCharacteristic characteristic)
1413{
1414 // Get native descriptor
1415 const auto charData = service->characteristicList.value(charHandle);
1416 if (!charData.descriptorList.contains(descHandle)) {
1417 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "cannot be found in characteristic"
1418 << charHandle;
1419 }
1420 const auto descData = charData.descriptorList.value(descHandle);
1421 const QBluetoothUuid descUuid = descData.uuid;
1422 if (descUuid ==
1423 QBluetoothUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration)) {
1424
1425 auto readOp = SAFE(characteristic.ReadClientCharacteristicConfigurationDescriptorAsync());
1426 if (!readOp)
1427 RETURN_SERVICE_ERROR("Could not read client characteristic configuration", service, QLowEnergyService::DescriptorReadError);
1428
1429 auto readCompletedLambda = [charHandle, descHandle, service]
1430 (IAsyncOperation<ClientCharConfigDescriptorResult> const &op, winrt::AsyncStatus const status)
1431 {
1432 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1433 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "read operation failed";
1434 service->setError(QLowEnergyService::DescriptorReadError);
1435 return;
1436 }
1437
1438 auto iValue = SAFE(op.GetResults());
1439 if (!iValue)
1440 RETURN_SERVICE_ERROR("Could not obtain result for descriptor", service, QLowEnergyService::DescriptorReadError);
1441 GattClientCharacteristicConfigurationDescriptorValue value;
1442 if (!TRY(value = iValue.ClientCharacteristicConfigurationDescriptor()))
1443 RETURN_SERVICE_ERROR("Could not obtain value for descriptor", service, QLowEnergyService::DescriptorReadError);
1444
1445 quint16 result = 0;
1446 bool correct = false;
1447 if (value & GattClientCharacteristicConfigurationDescriptorValue::Indicate) {
1448 result |= QLowEnergyCharacteristic::Indicate;
1449 correct = true;
1450 }
1451 if (value & GattClientCharacteristicConfigurationDescriptorValue::Notify) {
1452 result |= QLowEnergyCharacteristic::Notify;
1453 correct = true;
1454 }
1455 if (value == GattClientCharacteristicConfigurationDescriptorValue::None)
1456 correct = true;
1457 if (!correct) {
1458 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle
1459 << "read operation failed. Obtained unexpected value.";
1460 service->setError(QLowEnergyService::DescriptorReadError);
1461 return;
1462 }
1464 descData.uuid = QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration;
1465 descData.value = QByteArray(2, Qt::Uninitialized);
1466 qToLittleEndian(result, descData.value.data());
1467 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
1468 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
1469 descData.value);
1470 return;
1471 };
1472
1473 if (!TRY(readOp.Completed(readCompletedLambda)))
1474 RETURN_SERVICE_ERROR("Could not register descriptor read callback", service, QLowEnergyService::DescriptorReadError);
1475
1476 return;
1477 }
1478
1479 auto result = SAFE(await(characteristic.GetDescriptorsForUuidAsync(GUID(descData.uuid))));
1480 if (!result)
1481 RETURN_SERVICE_ERROR("Could not obtain descriptor for uuid", service, QLowEnergyService::DescriptorReadError);
1482
1483 GattCommunicationStatus commStatus;
1484 if (!TRY(commStatus = result.Status()) || commStatus != GattCommunicationStatus::Success) {
1485 qErrnoWarning("Could not obtain list of descriptors");
1486 service->setError(QLowEnergyService::DescriptorReadError);
1487 return;
1488 }
1489
1490 auto descriptors = SAFE(result.Descriptors());
1491 if (!descriptors)
1492 RETURN_SERVICE_ERROR("Could not obtain descriptor list", service, QLowEnergyService::DescriptorReadError);
1493
1494 uint size;
1495 if (!TRY(size = descriptors.Size()))
1496 RETURN_SERVICE_ERROR("Could not obtain descriptor list size", service, QLowEnergyService::DescriptorReadError);
1497
1498 if (size == 0) {
1499 qCWarning(QT_BT_WINDOWS) << "No descriptor with uuid" << descData.uuid << "was found.";
1500 service->setError(QLowEnergyService::DescriptorReadError);
1501 return;
1502 } else if (size > 1) {
1503 qCWarning(QT_BT_WINDOWS) << "There is more than 1 descriptor with uuid" << descData.uuid;
1504 }
1505
1506 auto descriptor = SAFE(descriptors.GetAt(0));
1507 if (!descriptor)
1508 RETURN_SERVICE_ERROR("Could not obtain descriptor from list", service, QLowEnergyService::DescriptorReadError);
1509
1510 auto readOp = SAFE(descriptor.ReadValueAsync(BluetoothCacheMode::Uncached));
1511 if (!readOp)
1512 RETURN_SERVICE_ERROR("Could not read descriptor value", service, QLowEnergyService::DescriptorReadError);
1513
1514 auto readCompletedLambda = [charHandle, descHandle, descUuid, service]
1515 (IAsyncOperation<GattReadResult> const &op, winrt::AsyncStatus const status)
1516 {
1517 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1518 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "read operation failed";
1519 service->setError(QLowEnergyService::DescriptorReadError);
1520 return;
1521 }
1522
1523 auto descriptorValue = SAFE(op.GetResults());
1524 if (!descriptorValue) {
1525 qCDebug(QT_BT_WINDOWS) << "Could not obtain result for descriptor" << descHandle;
1526 service->setError(QLowEnergyService::DescriptorReadError);
1527 return;
1528 }
1530 descData.uuid = descUuid;
1531 if (descData.uuid == QBluetoothUuid::DescriptorType::CharacteristicUserDescription)
1532 descData.value = byteArrayFromGattResult(descriptorValue, true);
1533 else
1534 descData.value = byteArrayFromGattResult(descriptorValue);
1535 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
1536 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
1537 descData.value);
1538 };
1539 if (!TRY(readOp.Completed(readCompletedLambda)))
1540 RETURN_SERVICE_ERROR("Could not register descriptor read callback", service, QLowEnergyService::DescriptorReadError);
1541}
1542
1543void QLowEnergyControllerPrivateWinRT::writeCharacteristic(
1544 const QSharedPointer<QLowEnergyServicePrivate> service,
1545 const QLowEnergyHandle charHandle,
1546 const QByteArray &newValue,
1547 QLowEnergyService::WriteMode mode)
1548{
1549 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << service << charHandle << newValue << mode;
1550 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1551 << QThread::currentThread();
1552 Q_ASSERT(!service.isNull());
1553 if (role == QLowEnergyController::PeripheralRole) {
1554 service->setError(QLowEnergyService::CharacteristicWriteError);
1555 Q_UNIMPLEMENTED();
1556 return;
1557 }
1558 if (!service->characteristicList.contains(charHandle)) {
1559 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charHandle << "cannot be found in service"
1560 << service->uuid;
1561 service->setError(QLowEnergyService::CharacteristicWriteError);
1562 return;
1563 }
1564
1565 QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
1566 const bool writeWithResponse = mode == QLowEnergyService::WriteWithResponse;
1567 if (!(charData.properties & (writeWithResponse ? QLowEnergyCharacteristic::Write
1568 : QLowEnergyCharacteristic::WriteNoResponse)))
1569 qCDebug(QT_BT_WINDOWS) << "Write flag is not set for characteristic" << charHandle;
1570
1571 auto characteristicCallback = [charHandle, service, newValue, writeWithResponse, this](
1572 GattCharacteristic characteristic) {
1573 writeCharacteristicHelper(service, charHandle, newValue, writeWithResponse,
1574 characteristic);
1575 };
1576
1577 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1578 qCDebug(QT_BT_WINDOWS) << "Could not obtain native characteristic" << charData.uuid
1579 << "from service" << service->uuid;
1580 service->setError(QLowEnergyService::CharacteristicWriteError);
1581 }
1582}
1583
1584void QLowEnergyControllerPrivateWinRT::writeCharacteristicHelper(
1585 const QSharedPointer<QLowEnergyServicePrivate> service,
1586 const QLowEnergyHandle charHandle, const QByteArray &newValue,
1587 bool writeWithResponse, GattCharacteristic characteristic)
1588{
1589 const quint32 length = quint32(newValue.length());
1590 Buffer buffer = nullptr;
1591 if (!TRY(buffer = Buffer(length)))
1592 RETURN_SERVICE_ERROR("Could not create buffer", service, QLowEnergyService::CharacteristicWriteError);
1593
1594 byte *bytes;
1595 if (!TRY(bytes = buffer.data()))
1596 RETURN_SERVICE_ERROR("Could not set buffer", service, QLowEnergyService::CharacteristicWriteError);
1597
1598 memcpy(bytes, newValue, length);
1599 if (!TRY(buffer.Length(length)))
1600 RETURN_SERVICE_ERROR("Could not set buffer length", service, QLowEnergyService::CharacteristicWriteError);
1601
1602 GattWriteOption option = writeWithResponse ? GattWriteOption::WriteWithResponse
1603 : GattWriteOption::WriteWithoutResponse;
1604 auto writeOp = SAFE(characteristic.WriteValueAsync(buffer, option));
1605 if (!writeOp)
1606 RETURN_SERVICE_ERROR("Could not write characteristic", service, QLowEnergyService::CharacteristicWriteError);
1607
1608 const auto charData = service->characteristicList.value(charHandle);
1609 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(this);
1610 auto writeCompletedLambda =
1611 [charData, charHandle, newValue, service, writeWithResponse, thisPtr]
1612 (IAsyncOperation<GattCommunicationStatus> const &op, winrt::AsyncStatus const status)
1613 {
1614 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1615 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charHandle
1616 << "write operation failed (async status)";
1617 service->setError(QLowEnergyService::CharacteristicWriteError);
1618 return;
1619 }
1620
1621 GattCommunicationStatus result;
1622 auto hr = HR(result = op.GetResults());
1623 if (hr == E_BLUETOOTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH) {
1624 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charHandle
1625 << "write operation was tried with invalid value length";
1626 service->setError(QLowEnergyService::CharacteristicWriteError);
1627 return;
1628 } else if (FAILED(hr)) {
1629 RETURN_SERVICE_ERROR("Could not obtain characteristic write result", service, QLowEnergyService::CharacteristicWriteError);
1630 }
1631
1632 if (result != GattCommunicationStatus::Success) {
1633 qCDebug(QT_BT_WINDOWS) << "Characteristic" << charHandle
1634 << "write operation failed (communication status)";
1635 service->setError(QLowEnergyService::CharacteristicWriteError);
1636 return;
1637 }
1638 // only update cache when property is readable. Otherwise it remains
1639 // empty.
1640 if (thisPtr && charData.properties & QLowEnergyCharacteristic::Read)
1641 thisPtr->updateValueOfCharacteristic(charHandle, newValue, false);
1642 if (writeWithResponse) {
1643 emit service->characteristicWritten(QLowEnergyCharacteristic(service, charHandle),
1644 newValue);
1645 }
1646 };
1647
1648 if (!TRY(writeOp.Completed(writeCompletedLambda)))
1649 RETURN_SERVICE_ERROR("Could not register characteristic write callback", service, QLowEnergyService::CharacteristicWriteError);
1650}
1651
1652void QLowEnergyControllerPrivateWinRT::writeDescriptor(
1653 const QSharedPointer<QLowEnergyServicePrivate> service,
1654 const QLowEnergyHandle charHandle,
1655 const QLowEnergyHandle descHandle,
1656 const QByteArray &newValue)
1657{
1658 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << service << charHandle << descHandle << newValue;
1659 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1660 << QThread::currentThread();
1661 Q_ASSERT(!service.isNull());
1662 if (role == QLowEnergyController::PeripheralRole) {
1663 service->setError(QLowEnergyService::DescriptorWriteError);
1664 Q_UNIMPLEMENTED();
1665 return;
1666 }
1667
1668 if (!service->characteristicList.contains(charHandle)) {
1669 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "in characteristic" << charHandle
1670 << "could not be found in service" << service->uuid;
1671 service->setError(QLowEnergyService::DescriptorWriteError);
1672 return;
1673 }
1674
1675 const auto charData = service->characteristicList.value(charHandle);
1676
1677 auto characteristicCallback = [descHandle, charHandle, service, newValue, this](
1678 GattCharacteristic characteristic) {
1679 writeDescriptorHelper(service, charHandle, descHandle, newValue, characteristic);
1680 };
1681
1682 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1683 qCDebug(QT_BT_WINDOWS) << "Could not obtain native characteristic" << charData.uuid
1684 << "from service" << service->uuid;
1685 service->setError(QLowEnergyService::DescriptorWriteError);
1686 }
1687}
1688
1689void QLowEnergyControllerPrivateWinRT::writeDescriptorHelper(
1690 const QSharedPointer<QLowEnergyServicePrivate> service,
1691 const QLowEnergyHandle charHandle,
1692 const QLowEnergyHandle descHandle,
1693 const QByteArray &newValue,
1694 GattCharacteristic characteristic)
1695{
1696 // Get native descriptor
1697 const auto charData = service->characteristicList.value(charHandle);
1698 if (!charData.descriptorList.contains(descHandle)) {
1699 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle
1700 << "could not be found in Characteristic" << charHandle;
1701 }
1702
1703 QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle);
1704 if (descData.uuid ==
1705 QBluetoothUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration)) {
1706 GattClientCharacteristicConfigurationDescriptorValue value;
1707 quint16 intValue = qFromLittleEndian<quint16>(newValue);
1708 if ((intValue & GattClientCharacteristicConfigurationDescriptorValue::Indicate)
1709 && (intValue & GattClientCharacteristicConfigurationDescriptorValue::Notify)) {
1710 qCWarning(QT_BT_WINDOWS) << "Setting both Indicate and Notify "
1711 "is not supported on WinRT";
1712 value = GattClientCharacteristicConfigurationDescriptorValue(
1713 (GattClientCharacteristicConfigurationDescriptorValue::Indicate
1714 | GattClientCharacteristicConfigurationDescriptorValue::Notify));
1715 } else if (intValue & GattClientCharacteristicConfigurationDescriptorValue::Indicate) {
1716 value = GattClientCharacteristicConfigurationDescriptorValue::Indicate;
1717 } else if (intValue & GattClientCharacteristicConfigurationDescriptorValue::Notify) {
1718 value = GattClientCharacteristicConfigurationDescriptorValue::Notify;
1719 } else if (intValue == 0) {
1720 value = GattClientCharacteristicConfigurationDescriptorValue::None;
1721 } else {
1722 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle
1723 << "write operation failed: Invalid value";
1724 service->setError(QLowEnergyService::DescriptorWriteError);
1725 return;
1726 }
1727
1728 auto writeOp = SAFE(characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(value));
1729 if (!writeOp)
1730 RETURN_SERVICE_ERROR("Could not write client characteristic configuration", service, QLowEnergyService::DescriptorWriteError);
1731
1732 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(this);
1733 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
1734 (IAsyncOperation<GattCommunicationStatus> const &op, winrt::AsyncStatus const status)
1735 {
1736 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1737 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "write operation failed";
1738 service->setError(QLowEnergyService::DescriptorWriteError);
1739 return;
1740 }
1741
1742 GattCommunicationStatus result;
1743 if (!TRY(result = op.GetResults()))
1744 RETURN_SERVICE_ERROR("Could not obtain result for descriptor", service, QLowEnergyService::DescriptorWriteError);
1745
1746 if (result != GattCommunicationStatus::Success) {
1747 qCWarning(QT_BT_WINDOWS) << "Descriptor" << descHandle << "write operation failed";
1748 service->setError(QLowEnergyService::DescriptorWriteError);
1749 return;
1750 }
1751 if (thisPtr)
1752 thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue, false);
1753 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
1754 newValue);
1755 };
1756
1757 if (!TRY(writeOp.Completed(writeCompletedLambda)))
1758 RETURN_SERVICE_ERROR("Could not register descriptor write callback", service, QLowEnergyService::DescriptorWriteError);
1759
1760 return;
1761 }
1762
1763 auto result = SAFE(await(characteristic.GetDescriptorsForUuidAsync(GUID(descData.uuid))));
1764 if (!result)
1765 RETURN_SERVICE_ERROR("Could not obtain descriptor from Uuid", service, QLowEnergyService::DescriptorWriteError);
1766
1767 GattCommunicationStatus commStatus;
1768 if (!TRY(commStatus = result.Status()) || commStatus != GattCommunicationStatus::Success) {
1769 qCWarning(QT_BT_WINDOWS) << "Descriptor operation failed";
1770 service->setError(QLowEnergyService::DescriptorWriteError);
1771 return;
1772 }
1773
1774 auto descriptors = SAFE(result.Descriptors());
1775 if (!descriptors)
1776 RETURN_SERVICE_ERROR("Could not obtain list of descriptors", service, QLowEnergyService::DescriptorWriteError);
1777
1778 uint size;
1779 if (!TRY(size = descriptors.Size()))
1780 RETURN_SERVICE_ERROR("Could not obtain list of descriptors' size", service, QLowEnergyService::DescriptorWriteError);
1781
1782 if (size == 0) {
1783 qCWarning(QT_BT_WINDOWS) << "No descriptor with uuid" << descData.uuid << "was found.";
1784 return;
1785 } else if (size > 1) {
1786 qCWarning(QT_BT_WINDOWS) << "There is more than 1 descriptor with uuid" << descData.uuid;
1787 }
1788
1789 auto descriptor = SAFE(descriptors.GetAt(0));
1790 if (!descriptor)
1791 RETURN_SERVICE_ERROR("Could not obtain descriptor", service, QLowEnergyService::DescriptorWriteError);
1792
1793 const quint32 length = quint32(newValue.length());
1794 Buffer buffer = nullptr;
1795 if (!TRY(buffer = Buffer(length)))
1796 RETURN_SERVICE_ERROR("Could not create buffer", service, QLowEnergyService::CharacteristicWriteError);
1797
1798 byte *bytes;
1799 if (!TRY(bytes = buffer.data()))
1800 RETURN_SERVICE_ERROR("Could not set buffer", service, QLowEnergyService::CharacteristicWriteError);
1801
1802 memcpy(bytes, newValue, length);
1803 if (!TRY(buffer.Length(length)))
1804 RETURN_SERVICE_ERROR("Could not set buffer length", service, QLowEnergyService::CharacteristicWriteError);
1805
1806 auto writeOp = SAFE(descriptor.WriteValueAsync(buffer));
1807 if (!writeOp)
1808 RETURN_SERVICE_ERROR("Could not write descriptor value", service, QLowEnergyService::CharacteristicWriteError);
1809
1810 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(this);
1811 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
1812 (IAsyncOperation<GattCommunicationStatus> const &op, winrt::AsyncStatus const status)
1813 {
1814 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1815 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "write operation failed";
1816 service->setError(QLowEnergyService::DescriptorWriteError);
1817 return;
1818 }
1819
1820 GattCommunicationStatus result;
1821 if (!TRY(result = op.GetResults()))
1822 RETURN_SERVICE_ERROR("Could not obtain result for descriptor", service, QLowEnergyService::DescriptorWriteError);
1823
1824 if (result != GattCommunicationStatus::Success) {
1825 qCDebug(QT_BT_WINDOWS) << "Descriptor" << descHandle << "write operation failed";
1826 service->setError(QLowEnergyService::DescriptorWriteError);
1827 return;
1828 }
1829 if (thisPtr)
1830 thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue, false);
1831 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
1832 newValue);
1833 };
1834
1835 if (!TRY(writeOp.Completed(writeCompletedLambda)))
1836 RETURN_SERVICE_ERROR("Could not register descriptor write callback", service, QLowEnergyService::DescriptorWriteError);
1837
1838}
1839
1840void QLowEnergyControllerPrivateWinRT::addToGenericAttributeList(const QLowEnergyServiceData &,
1842{
1843 Q_UNIMPLEMENTED();
1844}
1845
1846int QLowEnergyControllerPrivateWinRT::mtu() const
1847{
1848 uint16_t mtu = 23;
1849 if (!mGattSession) {
1850 qCDebug(QT_BT_WINDOWS) << "mtu queried before GattSession available. Using default mtu.";
1851 return mtu;
1852 }
1853
1854 if (!TRY(mtu = mGattSession.MaxPduSize()))
1855 RETURN_FALSE("could not obtain MTU size");
1856
1857 qCDebug(QT_BT_WINDOWS) << "mtu determined to be" << mtu;
1858 return mtu;
1859}
1860
1861void QLowEnergyControllerPrivateWinRT::handleCharacteristicChanged(
1862 quint16 charHandle, const QByteArray &data)
1863{
1864 qCDebug(QT_BT_WINDOWS) << __FUNCTION__ << charHandle << data;
1865 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1866 << QThread::currentThread();
1867 QSharedPointer<QLowEnergyServicePrivate> service =
1868 serviceForHandle(charHandle);
1869 if (service.isNull())
1870 return;
1871
1872 qCDebug(QT_BT_WINDOWS) << "Characteristic change notification" << service->uuid
1873 << charHandle << data.toHex();
1874
1875 QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle);
1876 if (!characteristic.isValid()) {
1877 qCWarning(QT_BT_WINDOWS) << "characteristicChanged: Cannot find characteristic";
1878 return;
1879 }
1880
1881 // only update cache when property is readable. Otherwise it remains
1882 // empty.
1883 if (characteristic.properties() & QLowEnergyCharacteristic::Read)
1884 updateValueOfCharacteristic(characteristic.attributeHandle(),
1885 data, false);
1886 emit service->characteristicChanged(characteristic, data);
1887}
1888
1889void QLowEnergyControllerPrivateWinRT::handleServiceHandlerError(const QString &error)
1890{
1891 if (state != QLowEnergyController::DiscoveringState)
1892 return;
1893
1894 qCWarning(QT_BT_WINDOWS) << "Error while discovering services:" << error;
1895 setState(QLowEnergyController::UnconnectedState);
1896 setError(QLowEnergyController::ConnectionError);
1897}
1898
1899void QLowEnergyControllerPrivateWinRT::handleConnectionError(const char *logMessage)
1900{
1901 qCWarning(QT_BT_WINDOWS) << logMessage;
1902 setError(QLowEnergyController::ConnectionError);
1903 setState(QLowEnergyController::UnconnectedState);
1904 unregisterFromStatusChanges();
1905 unregisterFromMtuChanges();
1906}
1907
1908QT_END_NAMESPACE
1909
1910#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)