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>
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>
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>
32#include <windows.devices.bluetooth.h>
33#include <windows.devices.bluetooth.genericattributeprofile.h>
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;
51 std::enable_if_t<std::is_enum_v<E>,
int> = 0>
54 return static_cast<std::underlying_type_t<E>>(x) &
static_cast<std::underlying_type_t<E>>(y);
57template<
typename T,
typename E,
58 std::enable_if_t<std::conjunction_v<std::is_integral<T>, std::is_enum<E>>,
int> = 0>
61 return x &
static_cast<std::underlying_type_t<E>>(y);
65 std::enable_if_t<std::is_enum_v<E>,
int> = 0>
68 return static_cast<std::underlying_type_t<E>>(x) |
static_cast<std::underlying_type_t<E>>(y);
71template<
typename T,
typename E,
72 std::enable_if_t<std::conjunction_v<std::is_integral<T>, std::is_enum<E>>,
int> = 0>
75 return x |
static_cast<std::underlying_type_t<E>>(y);
78template<
typename T,
typename E,
79 std::enable_if_t<std::conjunction_v<std::is_integral<T>, std::is_enum<E>>,
int> = 0>
82 a |=
static_cast<std::underlying_type_t<E>>(b);
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
90 constexpr T operator
&(T x, E y) { return bitwise_and<T, E>(x, y); } template
93 constexpr T operator
&(E x, T y) { return bitwise_and<T, E>(y, x); } inline
95 constexpr std::underlying_type_t<E> operator
|(E x, E y) { return bitwise_or<E>(x, y); } template
98 constexpr T operator
|(T x, E y) { return bitwise_or<T, E>(x, y); } template
101 constexpr T operator
|(E x, T y) { return bitwise_or<T, E>(y, x); } template
104 constexpr T &operator
|=(T &a, const E &b) { return bitwise_or_equal(a, b); }
113static constexpr bool never() {
return false; }
118 QDeadlineTimer awaitTime(timeout);
119 auto *dispatch = QAbstractEventDispatcher::instance();
121 dispatch = QCoreApplication::eventDispatcher();
123 QThread::yieldCurrentThread();
124 if (asyncInfo.Status() != winrt::AsyncStatus::Started) {
125 check_hresult(asyncInfo.ErrorCode());
126 return asyncInfo.GetResults();
129 dispatch->processEvents(QEventLoop::AllEvents);
130 }
while (!canceled() && !awaitTime.hasExpired());
132 throw hresult_error(E_ABORT);
143#define WARN_AND_CONTINUE(msg)
145 qCWarning(QT_BT_WINDOWS) << msg;
149#define DEC_CHAR_COUNT_AND_CONTINUE(msg)
151 qCWarning(QT_BT_WINDOWS) << msg;
152 --mCharacteristicsCountToBeDiscovered;
156#define RETURN_SERVICE_ERROR(msg, service, error)
158 qCDebug(QT_BT_WINDOWS) << msg;
159 service->setError(error);
163#define RETURN_FALSE(msg)
169#define RETURN_MSG(msg)
175Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
176Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS_SERVICE_THREAD)
178static constexpr qint64 kMaxConnectTimeout = 20000;
183 qErrnoWarning(
"nullptr passed to byteArrayFromBuffer");
186 qsizetype size =
SAFE(buffer.Length());
190 QString valueString = QString::fromUtf16(
191 reinterpret_cast<
char16_t *>(buffer.data())).left(size / 2);
192 return valueString.toUtf8();
194 return QByteArray(
reinterpret_cast<
char *>(buffer.data()), size);
198 bool isWCharString =
false)
200 auto buffer =
SAFE(gattResult.Value());
202 qCWarning(QT_BT_WINDOWS) <<
"Could not obtain buffer from GattReadResult";
205 return byteArrayFromBuffer(buffer, isWCharString);
373 void emitErrorAndQuitThread(
const QString &error);
396 if (!mAbortRequested && (mCharacteristicsCountToBeDiscovered == 0)) {
397 emit charListObtained(mService, mCharacteristicList, mIndicateChars,
398 mStartHandle, mEndHandle);
400 QThread::currentThread()->quit();
406 emit errorOccured(error);
407 QThread::currentThread()->quit();
441 void connectToPairedDevice();
442 void connectToUnpairedDevice();
443 void emitErrorAndQuitThread(
const QString &error);
444 void emitErrorAndQuitThread(
const char *error);
445 void emitConnectedAndQuitThread();
447 BluetoothLEDevice mDevice =
nullptr;
448 GattSession mGattSession =
nullptr;
449 const QBluetoothAddress mAddress;
450 bool mAbortConnection =
false;
451 bool mComInitialized =
false;
456 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
457 mComInitialized =
TRY(winrt::init_apartment());
459 auto earlyExit = [
this]() {
return mAbortConnection; };
460 if (!(mDevice =
SAFE(await(BluetoothLEDevice::FromBluetoothAddressAsync(mAddress.toUInt64()), earlyExit))))
461 return emitErrorAndQuitThread(
"Could not find LE device from address");
464 BluetoothDeviceId deviceId =
SAFE(mDevice.BluetoothDeviceId());
466 return emitErrorAndQuitThread(
"Could not get device id");
469 if (!(mGattSession =
SAFE(await(GattSession::FromDeviceIdAsync(deviceId), earlyExit))))
470 return emitErrorAndQuitThread(
"Could not get GattSession from id");
472 BluetoothConnectionStatus status;
473 if (!
TRY(status = mDevice.ConnectionStatus()))
474 return emitErrorAndQuitThread(
"Could not get connection status");
475 if (status == BluetoothConnectionStatus::Connected)
476 return emitConnectedAndQuitThread();
478 QBluetoothLocalDevice localDevice;
479 QBluetoothLocalDevice::Pairing pairing = localDevice.pairingStatus(mAddress);
480 if (pairing == QBluetoothLocalDevice::Unpaired)
481 connectToUnpairedDevice();
483 connectToPairedDevice();
488 mAbortConnection =
true;
493 disconnect(
this, &QWinRTLowEnergyConnectionHandler::deviceConnected,
nullptr,
nullptr);
494 disconnect(
this, &QWinRTLowEnergyConnectionHandler::errorOccurred,
nullptr,
nullptr);
499 auto earlyExit = [
this]() {
return mAbortConnection; };
500 QDeadlineTimer deadline(kMaxConnectTimeout);
501 while (!mAbortConnection && !deadline.hasExpired()) {
503 auto deviceServicesResult =
SAFE(await(mDevice.GetGattServicesAsync(), earlyExit));
504 if (!deviceServicesResult)
505 return emitErrorAndQuitThread(
"Could not obtain services");
507 if (!
SAFE(deviceServicesResult.Status() == GattCommunicationStatus::Success))
508 return emitErrorAndQuitThread(
"Service operation failed");
510 auto deviceServices =
SAFE(deviceServicesResult.Services());
512 return emitErrorAndQuitThread(
"Could not obtain list of services");
515 if (!
TRY(serviceCount = deviceServices.Size()))
516 return emitErrorAndQuitThread(
"Could not obtain size of list of services");
517 if (serviceCount == 0)
518 return emitErrorAndQuitThread(
"Found devices without services");
522 for (uint i = 0; i < serviceCount; ++i) {
524 auto service =
SAFE(deviceServices.GetAt(i));
526 return emitErrorAndQuitThread(
"Could not obtain service");
528 auto characteristicsResult =
SAFE(await(service.GetCharacteristicsAsync(), earlyExit));
529 if (!characteristicsResult)
530 return emitErrorAndQuitThread(
"Could not obtain characteristic");
532 if (!
SAFE(characteristicsResult.Status() == GattCommunicationStatus::Success)) {
533 qCWarning(QT_BT_WINDOWS) <<
"Characteristic operation failed";
537 IVectorView<GattCharacteristic> characteristics =
nullptr;
538 auto hr =
HR(characteristics = characteristicsResult.Characteristics());
539 if (hr == E_ACCESSDENIED) {
542 return emitErrorAndQuitThread(
"Could not obtain characteristic list. "
543 "Please check your manifest capabilities");
544 }
else if (FAILED(hr) || !characteristics) {
545 return emitErrorAndQuitThread(
"Could not obtain characteristic list.");
548 uint characteristicsCount;
549 if (!
TRY(characteristicsCount = characteristics.Size()))
550 return emitErrorAndQuitThread(
"Could not obtain size of characteristic list.");
551 for (uint j = 0; j < characteristicsCount; ++j) {
553 auto characteristic =
SAFE(characteristics.GetAt(j));
555 return emitErrorAndQuitThread(
"Could not get characteristic");
556 GattReadResult result =
nullptr;
557 hr =
HR(result = await(
558 characteristic.ReadValueAsync(BluetoothCacheMode::Uncached), earlyExit));
559 if (hr == E_ILLEGAL_METHOD_CALL) {
563 }
else if (FAILED(hr) || !result) {
564 return emitErrorAndQuitThread(
"Could not read characteristic value");
567 auto buffer =
SAFE(result.Value());
569 qCDebug(QT_BT_WINDOWS) <<
"Problem reading value";
573 emitConnectedAndQuitThread();
581 emitErrorAndQuitThread(
"Connect to device failed due to timeout!");
586 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
588 auto earlyExit = [
this]() {
return mAbortConnection; };
589 QDeadlineTimer deadline(kMaxConnectTimeout);
590 while (!mAbortConnection && !deadline.hasExpired()) {
592 auto deviceServicesResult =
SAFE(await_forever(mDevice.GetGattServicesAsync(), earlyExit));
593 if (!deviceServicesResult)
594 return emitErrorAndQuitThread(
"Could not obtain services");
596 GattCommunicationStatus commStatus;
597 if (!
TRY(commStatus = deviceServicesResult.Status()))
598 return emitErrorAndQuitThread(
"Could not obtain comm status");
599 if (commStatus == GattCommunicationStatus::Unreachable)
601 if (commStatus != GattCommunicationStatus::Success)
602 return emitErrorAndQuitThread(
"Service operation failed");
604 emitConnectedAndQuitThread();
610 emitErrorAndQuitThread(
"Connect to device failed due to timeout!");
615 emit errorOccurred(error);
616 QThread::currentThread()->quit();
621 emitErrorAndQuitThread(QString::fromUtf8(error));
626 emit deviceConnected(mDevice.as<abi::BluetoothLEDevice>(), mGattSession.as<abi::GattSession>());
627 QThread::currentThread()->quit();
630QLowEnergyControllerPrivateWinRT::QLowEnergyControllerPrivateWinRT()
634 connect(
this, &QLowEnergyControllerPrivateWinRT::characteristicChanged,
635 this, &QLowEnergyControllerPrivateWinRT::handleCharacteristicChanged,
636 Qt::QueuedConnection);
641 unregisterFromStatusChanges();
642 unregisterFromValueChanges();
645void QLowEnergyControllerPrivateWinRT::
init()
651 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
652 if (remoteDevice.isNull()) {
653 qCWarning(QT_BT_WINDOWS) <<
"Invalid/null remote device address";
654 setError(QLowEnergyController::UnknownRemoteDeviceError);
657 setState(QLowEnergyController::ConnectingState);
660 QThread *thread =
new QThread;
661 worker->moveToThread(thread);
664 connect(thread, &QThread::started, worker, &QWinRTLowEnergyConnectionHandler::connectToDevice);
665 connect(thread, &QThread::finished, worker, &QObject::deleteLater);
666 connect(worker, &QObject::destroyed, thread, &QObject::deleteLater);
667 connect(worker, &QWinRTLowEnergyConnectionHandler::errorOccurred,
this,
668 [
this](
const QString &msg) { handleConnectionError(msg.toUtf8().constData()); });
669 connect(worker, &QWinRTLowEnergyConnectionHandler::deviceConnected,
this,
670 [
this](com_ptr<abi::BluetoothLEDevice> device, com_ptr<abi::GattSession> session) {
671 if (!device || !session) {
672 handleConnectionError(
"Failed to get device or gatt service");
675 mDevice = device.as<BluetoothLEDevice>();
676 mGattSession = session.as<GattSession>();
678 if (!registerForStatusChanges() || !registerForMtuChanges()) {
679 handleConnectionError(
"Failed to register for changes");
683 Q_Q(QLowEnergyController);
684 setState(QLowEnergyController::ConnectedState);
692 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
693 Q_Q(QLowEnergyController);
694 setState(QLowEnergyController::ClosingState);
695 emit abortConnection();
696 unregisterFromValueChanges();
697 unregisterFromStatusChanges();
698 unregisterFromMtuChanges();
700 mGattSession =
nullptr;
702 setState(QLowEnergyController::UnconnectedState);
703 emit q->disconnected();
706bool QLowEnergyControllerPrivateWinRT::getNativeService(
const QBluetoothUuid &serviceUuid,
707 NativeServiceCallback callback)
709 if (m_openedServices.contains(serviceUuid)) {
710 callback(m_openedServices.value(serviceUuid,
nullptr));
714 auto servicesResultOperation =
SAFE(mDevice.GetGattServicesForUuidAsync(GUID(serviceUuid)));
715 if (!servicesResultOperation)
718 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
719 bool ok =
TRY(servicesResultOperation.Completed(
720 [thisPtr, callback, &serviceUuid](
721 IAsyncOperation<GattDeviceServicesResult>
const &op, winrt::AsyncStatus
const status)
724 qCWarning(QT_BT_WINDOWS) <<
"LE controller was removed while getting native service";
728 if (status != winrt::AsyncStatus::Completed) {
729 qCDebug(QT_BT_WINDOWS) <<
"Failed to get result of async service request";
733 auto result =
SAFE(op.GetResults());
735 RETURN_MSG(
"Failed to get result of async service request");
737 auto services =
SAFE(result.Services());
739 RETURN_MSG(
"Failed to extract services from the result");
741 uint servicesCount = 0;
742 if (!
TRY(servicesCount = services.Size()))
743 RETURN_MSG(
"Failed to extract services count");
745 if (servicesCount > 0) {
746 if (servicesCount > 1) {
747 qWarning() <<
"getNativeService: more than one service detected for UUID"
748 << serviceUuid <<
"The first service will be used.";
751 auto service =
SAFE(services.GetAt(0));
753 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native service for Uuid"
756 thisPtr->m_openedServices.insert(serviceUuid, service);
760 qCWarning(QT_BT_WINDOWS) <<
"No services found for Uuid" << serviceUuid;
767bool QLowEnergyControllerPrivateWinRT::getNativeCharacteristic(
768 const QBluetoothUuid &serviceUuid,
const QBluetoothUuid &charUuid,
769 NativeCharacteristicCallback callback)
771 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
772 auto serviceCallback = [thisPtr, callback, charUuid](GattDeviceService service) {
774 auto characteristicRequestOp =
SAFE(service.GetCharacteristicsForUuidAsync(GUID(charUuid)));
775 if (!characteristicRequestOp)
776 RETURN_MSG(
"Could not start async characteristics request");
778 TRY(characteristicRequestOp.Completed(
780 IAsyncOperation<GattCharacteristicsResult>
const &op, winrt::AsyncStatus
const status)
785 if (status != winrt::AsyncStatus::Completed) {
786 qCDebug(QT_BT_WINDOWS) <<
"Failed to get result of async characteristic "
791 auto result =
SAFE(op.GetResults());
793 RETURN_MSG(
"Failed to get result of async characteristic operation");
795 GattCommunicationStatus commStatus;
796 if (!
TRY(commStatus = result.Status()) || commStatus != GattCommunicationStatus::Success) {
797 qErrnoWarning(
"Native characteristic operation failed.");
801 auto characteristics =
SAFE(result.Characteristics());
802 if (!characteristics)
803 RETURN_MSG(
"Could not obtain characteristic list.");
806 if (!
TRY(size = characteristics.Size()))
807 RETURN_MSG(
"Could not obtain characteristic list's size.");
810 qErrnoWarning(
"More than 1 characteristic found.");
812 auto characteristic =
SAFE(characteristics.GetAt(0));
814 RETURN_MSG(
"Could not obtain first characteristic for service");
816 callback(characteristic);
820 if (!getNativeService(serviceUuid, serviceCallback)) {
821 qCDebug(QT_BT_WINDOWS) <<
"Failed to get native service for" << serviceUuid;
828void QLowEnergyControllerPrivateWinRT::registerForValueChanges(
const QBluetoothUuid &serviceUuid,
829 const QBluetoothUuid &charUuid)
831 qCDebug(QT_BT_WINDOWS) <<
"Registering characteristic" << charUuid <<
"in service"
832 << serviceUuid <<
"for value changes";
833 for (
const ValueChangedEntry &entry : std::as_const(mValueChangedTokens)) {
835 if (!
TRY(guuid = entry.characteristic.Uuid()))
837 if (QBluetoothUuid(guuid) == charUuid)
841 auto callback = [
this, charUuid, serviceUuid](GattCharacteristic characteristic) {
842 winrt::event_token token;
843 if (!
TRY(token = characteristic.ValueChanged({
this, &QLowEnergyControllerPrivateWinRT::onValueChange })))
844 RETURN_MSG(
"Could not register characteristic for value changes");
846 mValueChangedTokens.append(ValueChangedEntry(characteristic, token));
847 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charUuid <<
"in service"
848 << serviceUuid <<
"registered for value changes";
851 if (!getNativeCharacteristic(serviceUuid, charUuid, callback)) {
852 qCDebug(QT_BT_WINDOWS).nospace() <<
"Could not obtain native characteristic "
853 << charUuid <<
" from service " << serviceUuid
854 <<
". Qt will not be able to signal"
855 <<
" changes for this characteristic.";
859void QLowEnergyControllerPrivateWinRT::unregisterFromValueChanges()
861 qCDebug(QT_BT_WINDOWS) <<
"Unregistering " << mValueChangedTokens.size() <<
" value change tokens";
862 for (
const ValueChangedEntry &entry : std::as_const(mValueChangedTokens)) {
863 if (!entry.characteristic) {
864 qCWarning(QT_BT_WINDOWS) <<
"Unregistering from value changes for characteristic failed."
865 <<
"Characteristic has been deleted";
868 if (!
TRY(entry.characteristic.ValueChanged(entry.token)))
869 qCWarning(QT_BT_WINDOWS) <<
"Unregistering from value changes for characteristic failed.";
871 mValueChangedTokens.clear();
874void QLowEnergyControllerPrivateWinRT::onValueChange(GattCharacteristic characteristic, GattValueChangedEventArgs
const &args)
877 if (!
TRY(handle = characteristic.AttributeHandle()))
878 RETURN_MSG(
"Could not obtain characteristic's handle");
880 auto buffer =
SAFE(args.CharacteristicValue());
882 RETURN_MSG(
"Could not obtain characteristic's value");
884 emit characteristicChanged(handle, byteArrayFromBuffer(buffer));
887bool QLowEnergyControllerPrivateWinRT::registerForMtuChanges()
889 if (!mDevice || !mGattSession)
891 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
892 if (!
TRY(mMtuChangedToken = mGattSession.MaxPduSizeChanged({
this, &QLowEnergyControllerPrivateWinRT::onMtuChange })))
897void QLowEnergyControllerPrivateWinRT::unregisterFromMtuChanges()
899 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
900 if (mDevice && mGattSession && mMtuChangedToken) {
901 TRY(mGattSession.MaxPduSizeChanged(mMtuChangedToken));
902 mMtuChangedToken = { 0 };
906void QLowEnergyControllerPrivateWinRT::onMtuChange(GattSession session, winrt::IInspectable args)
908 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
910 if (session != mGattSession) {
911 qCWarning(QT_BT_WINDOWS) <<
"Got MTU changed event for wrong or outdated GattSession.";
913 Q_Q(QLowEnergyController);
914 emit q->mtuChanged(mtu());
918bool QLowEnergyControllerPrivateWinRT::registerForStatusChanges()
923 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
925 if (!
TRY(mStatusChangedToken = mDevice.ConnectionStatusChanged({
this, &QLowEnergyControllerPrivateWinRT::onStatusChange })))
930void QLowEnergyControllerPrivateWinRT::unregisterFromStatusChanges()
932 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
933 if (mDevice && mStatusChangedToken) {
934 TRY(mDevice.ConnectionStatusChanged(mStatusChangedToken));
935 mStatusChangedToken = { 0 };
939void QLowEnergyControllerPrivateWinRT::onStatusChange(BluetoothLEDevice dev, winrt::IInspectable args)
941 Q_Q(QLowEnergyController);
943 BluetoothConnectionStatus status;
944 if (!
TRY(status = dev.ConnectionStatus()))
945 RETURN_MSG(
"Could not obtain connection status");
947 if (state == QLowEnergyController::ConnectingState
948 && status == BluetoothConnectionStatus::Connected) {
949 setState(QLowEnergyController::ConnectedState);
951 }
else if (state != QLowEnergyController::UnconnectedState
952 && status == BluetoothConnectionStatus::Disconnected) {
954 unregisterFromValueChanges();
955 unregisterFromStatusChanges();
956 unregisterFromMtuChanges();
957 mGattSession =
nullptr;
959 setError(QLowEnergyController::RemoteHostClosedError);
960 setState(QLowEnergyController::UnconnectedState);
961 emit q->disconnected();
965void QLowEnergyControllerPrivateWinRT::obtainIncludedServices(QSharedPointer<QLowEnergyServicePrivate> servicePointer, GattDeviceService service)
967 Q_Q(QLowEnergyController);
969 auto result =
SAFE(await(service.GetIncludedServicesAsync()));
971 RETURN_MSG(
"Could not obtain included services");
974 if (state != QLowEnergyController::DiscoveringState)
977 GattCommunicationStatus status;
978 if (!
TRY(status = result.Status())) {
979 qErrnoWarning(
"Could not obtain list of included services");
983 auto includedServices =
SAFE(result.Services());
984 if (!includedServices)
988 if (!
TRY(count = includedServices.Size()))
989 RETURN_MSG(
"Could not obtain service list's size");
991 for (uint i = 0; i < count; ++i) {
993 auto includedService =
SAFE(includedServices.GetAt(i));
994 if (!includedService)
998 if (!
TRY(guuid = includedService.Uuid()))
1001 const QBluetoothUuid includedUuid(guuid);
1002 QSharedPointer<QLowEnergyServicePrivate> includedPointer;
1003 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__
1004 <<
"Changing service pointer from thread"
1005 << QThread::currentThread();
1006 if (serviceList.contains(includedUuid)) {
1007 includedPointer = serviceList.value(includedUuid);
1010 priv->uuid = includedUuid;
1014 serviceList.insert(includedUuid, includedPointer);
1016 includedPointer->type |= QLowEnergyService::IncludedService;
1017 servicePointer->includedServices.append(includedUuid);
1019 obtainIncludedServices(includedPointer, includedService);
1021 emit q->serviceDiscovered(includedUuid);
1025void QLowEnergyControllerPrivateWinRT::onServiceDiscoveryFinished(IAsyncOperation<GattDeviceServicesResult>
const &op, winrt::AsyncStatus status)
1033 if (state != QLowEnergyController::DiscoveringState)
1036 Q_Q(QLowEnergyController);
1037 if (status != winrt::AsyncStatus::Completed) {
1038 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain services";
1042 auto result =
SAFE(op.GetResults());
1044 return handleConnectionError(
"Could not obtain service discovery result");
1046 GattCommunicationStatus commStatus;
1047 if (!
TRY(commStatus = result.Status()))
1048 return handleConnectionError(
"Could not obtain service discovery status");
1050 if (commStatus != GattCommunicationStatus::Success)
1053 auto deviceServices =
SAFE(result.Services());
1054 if (!deviceServices)
1055 return handleConnectionError(
"Could not obtain service list");
1058 if (!
TRY(serviceCount = deviceServices.Size()))
1059 return handleConnectionError(
"Could not obtain service list size");
1061 for (uint i = 0; i < serviceCount; ++i) {
1063 auto deviceService =
SAFE(deviceServices.GetAt(i));
1068 if (!
TRY(guuid = deviceService.Uuid()))
1071 const QBluetoothUuid service(guuid);
1072 m_openedServices.insert(service, deviceService);
1074 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__
1075 <<
"Changing service pointer from thread"
1076 << QThread::currentThread();
1077 QSharedPointer<QLowEnergyServicePrivate> pointer;
1078 if (serviceList.contains(service)) {
1079 pointer = serviceList.value(service);
1082 priv->uuid = service;
1086 serviceList.insert(service, pointer);
1088 pointer->type |= QLowEnergyService::PrimaryService;
1090 obtainIncludedServices(pointer, deviceService);
1094 if (state != QLowEnergyController::DiscoveringState) {
1095 emit q->discoveryFinished();
1100 emit q->serviceDiscovered(service);
1103 setState(QLowEnergyController::DiscoveredState);
1104 emit q->discoveryFinished();
1107void QLowEnergyControllerPrivateWinRT::clearAllServices()
1111 for (
auto &uuid : m_requestDetailsServiceUuids)
1112 m_openedServices.remove(uuid);
1113 m_requestDetailsServiceUuids.clear();
1115 for (
auto service : m_openedServices)
1116 TRY(service.Close());
1117 m_openedServices.clear();
1120void QLowEnergyControllerPrivateWinRT::closeAndRemoveService(
const QBluetoothUuid &uuid)
1122 auto record = m_openedServices.find(uuid);
1123 if (record != m_openedServices.end()) {
1124 auto service = record.value();
1125 m_openedServices.erase(record);
1127 TRY(service.Close());
1133 qCDebug(QT_BT_WINDOWS) <<
"Service discovery initiated";
1137 auto asyncResult =
SAFE(mDevice.GetGattServicesAsync());
1139 return handleConnectionError(
"Could not obtain services");
1141 if (!
TRY(asyncResult.Completed({
this, &QLowEnergyControllerPrivateWinRT::onServiceDiscoveryFinished })))
1142 return handleConnectionError(
"Could not register services discovery callback");
1146 const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode)
1148 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << service;
1149 if (!serviceList.contains(service)) {
1150 qCWarning(QT_BT_WINDOWS) <<
"Discovery done of unknown service:"
1151 << service.toString();
1156 closeAndRemoveService(service);
1158 auto serviceCallback = [service, mode,
this](GattDeviceService deviceService) {
1159 discoverServiceDetailsHelper(service, mode, deviceService);
1162 if (!getNativeService(service, serviceCallback))
1163 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native service for uuid " << service;
1166void QLowEnergyControllerPrivateWinRT::discoverServiceDetailsHelper(
1167 const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode,
1168 GattDeviceService deviceService)
1170 auto reactOnDiscoveryError = [](QSharedPointer<QLowEnergyServicePrivate> service,
1173 qCDebug(QT_BT_WINDOWS) << msg;
1174 service->setError(QLowEnergyService::UnknownError);
1175 service->setState(QLowEnergyService::RemoteService);
1178 QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
1180 qCDebug(QT_BT_WINDOWS) <<
"Device was disconnected while doing service discovery";
1183 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1184 << QThread::currentThread();
1185 pointer->setState(QLowEnergyService::RemoteServiceDiscovering);
1187 auto result =
SAFE(await_forever(deviceService.GetIncludedServicesAsync()));
1189 return reactOnDiscoveryError(pointer,
"Could not obtain included service list");
1191 if (
SAFE(result.Status() != GattCommunicationStatus::Success))
1192 return reactOnDiscoveryError(pointer,
"Obtaining list of included services failed");
1194 auto deviceServices =
SAFE(result.Services());
1195 if (!deviceServices)
1196 return reactOnDiscoveryError(pointer,
"Could not obtain service list from result");
1199 if (!
TRY(serviceCount = deviceServices.Size()))
1200 return reactOnDiscoveryError(pointer,
"Could not obtain included service list's size");
1202 for (uint i = 0; i < serviceCount; ++i) {
1204 auto includedService =
SAFE(deviceServices.GetAt(i));
1205 if (!includedService)
1209 if (!
TRY(guuid = includedService.Uuid()))
1212 const QBluetoothUuid service(guuid);
1213 if (service.isNull()) {
1214 qCDebug(QT_BT_WINDOWS) <<
"Could not find service";
1218 pointer->includedServices.append(service);
1221 QSharedPointer<QLowEnergyServicePrivate> otherService = serviceList.value(service);
1222 if (!otherService.isNull())
1223 otherService->type |= QLowEnergyService::IncludedService;
1228 m_requestDetailsServiceUuids.insert(service);
1229 QThread *thread =
new QThread;
1230 worker->moveToThread(thread);
1231 connect(thread, &QThread::started, worker, &QWinRTLowEnergyServiceHandler::obtainCharList);
1232 connect(thread, &QThread::finished, worker, &QObject::deleteLater);
1233 connect(worker, &QObject::destroyed, thread, &QObject::deleteLater);
1235 worker, &QWinRTLowEnergyServiceHandler::setAbortRequested);
1236 connect(worker, &QWinRTLowEnergyServiceHandler::errorOccured,
1237 this, &QLowEnergyControllerPrivateWinRT::handleServiceHandlerError);
1238 connect(worker, &QWinRTLowEnergyServiceHandler::charListObtained,
this,
1239 [
this](
const QBluetoothUuid &service, QHash<QLowEnergyHandle,
1240 QLowEnergyServicePrivate::CharData> charList, QList<QBluetoothUuid> indicateChars,
1241 QLowEnergyHandle startHandle, QLowEnergyHandle endHandle) {
1242 if (!serviceList.contains(service)) {
1243 qCWarning(QT_BT_WINDOWS)
1244 <<
"Discovery complete for unknown service:" << service.toString();
1247 m_requestDetailsServiceUuids.remove(service);
1249 QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
1250 pointer->startHandle = startHandle;
1251 pointer->endHandle = endHandle;
1252 pointer->characteristicList = charList;
1254 for (
const QBluetoothUuid &indicateChar : std::as_const(indicateChars))
1255 registerForValueChanges(service, indicateChar);
1257 pointer->setState(QLowEnergyService::RemoteServiceDiscovered);
1263 const QLowEnergyAdvertisingParameters &,
1264 const QLowEnergyAdvertisingData &,
1265 const QLowEnergyAdvertisingData &)
1267 setError(QLowEnergyController::AdvertisingError);
1282 const QSharedPointer<QLowEnergyServicePrivate> service,
1283 const QLowEnergyHandle charHandle)
1285 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << service << charHandle;
1286 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1287 << QThread::currentThread();
1288 Q_ASSERT(!service.isNull());
1289 if (role == QLowEnergyController::PeripheralRole) {
1290 service->setError(QLowEnergyService::CharacteristicReadError);
1295 if (!service->characteristicList.contains(charHandle)) {
1296 qCDebug(QT_BT_WINDOWS) << charHandle <<
"could not be found in service" << service->uuid;
1297 service->setError(QLowEnergyService::CharacteristicReadError);
1301 const auto charData = service->characteristicList.value(charHandle);
1302 if (!(charData.properties & QLowEnergyCharacteristic::Read))
1303 qCDebug(QT_BT_WINDOWS) <<
"Read flag is not set for characteristic" << charData.uuid;
1305 auto characteristicCallback = [charHandle, service,
this](
1306 GattCharacteristic characteristic) {
1307 readCharacteristicHelper(service, charHandle, characteristic);
1310 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1311 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native characteristic" << charData.uuid
1312 <<
"from service" << service->uuid;
1313 service->setError(QLowEnergyService::CharacteristicReadError);
1317void QLowEnergyControllerPrivateWinRT::readCharacteristicHelper(
1318 const QSharedPointer<QLowEnergyServicePrivate> service,
1319 const QLowEnergyHandle charHandle,
1320 GattCharacteristic characteristic)
1322 auto readOp =
SAFE(characteristic.ReadValueAsync(BluetoothCacheMode::Uncached));
1324 RETURN_SERVICE_ERROR(
"Could not read characteristic", service, QLowEnergyService::CharacteristicReadError);
1326 auto readCompletedLambda = [charHandle, service]
1327 (IAsyncOperation<GattReadResult>
const &op, winrt::AsyncStatus
const status)
1329 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1330 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle <<
"read operation failed.";
1331 service->setError(QLowEnergyService::CharacteristicReadError);
1334 auto characteristicValue =
SAFE(op.GetResults());
1335 if (!characteristicValue)
1336 RETURN_SERVICE_ERROR(
"Could not obtain result for characteristic", service, QLowEnergyService::CharacteristicReadError);
1338 const QByteArray value = byteArrayFromGattResult(characteristicValue);
1339 auto charData = service->characteristicList.value(charHandle);
1340 charData.value = value;
1341 service->characteristicList.insert(charHandle, charData);
1342 emit service->characteristicRead(QLowEnergyCharacteristic(service, charHandle), value);
1346 if (!
TRY(readOp.Completed(readCompletedLambda)))
1347 RETURN_SERVICE_ERROR(
"Could not register characteristic read callback", service, QLowEnergyService::CharacteristicReadError);
1351 const QSharedPointer<QLowEnergyServicePrivate> service,
1352 const QLowEnergyHandle charHandle,
1353 const QLowEnergyHandle descHandle)
1355 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << service << charHandle << descHandle;
1356 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1357 << QThread::currentThread();
1358 Q_ASSERT(!service.isNull());
1359 if (role == QLowEnergyController::PeripheralRole) {
1360 service->setError(QLowEnergyService::DescriptorReadError);
1365 if (!service->characteristicList.contains(charHandle)) {
1366 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"in characteristic" << charHandle
1367 <<
"cannot be found in service" << service->uuid;
1368 service->setError(QLowEnergyService::DescriptorReadError);
1372 const auto charData = service->characteristicList.value(charHandle);
1374 auto characteristicCallback = [charHandle, descHandle, service,
this](
1375 GattCharacteristic characteristic) {
1376 readDescriptorHelper(service, charHandle, descHandle, characteristic);
1379 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1380 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native characteristic" << charData.uuid
1381 <<
"from service" << service->uuid;
1382 service->setError(QLowEnergyService::DescriptorReadError);
1386void QLowEnergyControllerPrivateWinRT::readDescriptorHelper(
1387 const QSharedPointer<QLowEnergyServicePrivate> service,
1388 const QLowEnergyHandle charHandle,
1389 const QLowEnergyHandle descHandle,
1390 GattCharacteristic characteristic)
1393 const auto charData = service->characteristicList.value(charHandle);
1394 if (!charData.descriptorList.contains(descHandle)) {
1395 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"cannot be found in characteristic"
1398 const auto descData = charData.descriptorList.value(descHandle);
1399 const QBluetoothUuid descUuid = descData.uuid;
1401 QBluetoothUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration)) {
1403 auto readOp =
SAFE(characteristic.ReadClientCharacteristicConfigurationDescriptorAsync());
1405 RETURN_SERVICE_ERROR(
"Could not read client characteristic configuration", service, QLowEnergyService::DescriptorReadError);
1407 auto readCompletedLambda = [charHandle, descHandle, service]
1408 (IAsyncOperation<ClientCharConfigDescriptorResult>
const &op, winrt::AsyncStatus
const status)
1410 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1411 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"read operation failed";
1412 service->setError(QLowEnergyService::DescriptorReadError);
1416 auto iValue =
SAFE(op.GetResults());
1418 RETURN_SERVICE_ERROR(
"Could not obtain result for descriptor", service, QLowEnergyService::DescriptorReadError);
1419 GattClientCharacteristicConfigurationDescriptorValue value;
1420 if (!
TRY(value = iValue.ClientCharacteristicConfigurationDescriptor()))
1421 RETURN_SERVICE_ERROR(
"Could not obtain value for descriptor", service, QLowEnergyService::DescriptorReadError);
1424 bool correct =
false;
1425 if (value & GattClientCharacteristicConfigurationDescriptorValue::Indicate) {
1426 result |= QLowEnergyCharacteristic::Indicate;
1429 if (value & GattClientCharacteristicConfigurationDescriptorValue::Notify) {
1430 result |= QLowEnergyCharacteristic::Notify;
1433 if (value == GattClientCharacteristicConfigurationDescriptorValue::None)
1436 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle
1437 <<
"read operation failed. Obtained unexpected value.";
1438 service->setError(QLowEnergyService::DescriptorReadError);
1442 descData.uuid = QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration;
1443 descData.value = QByteArray(2, Qt::Uninitialized);
1444 qToLittleEndian(result, descData.value.data());
1445 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
1446 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
1451 if (!
TRY(readOp.Completed(readCompletedLambda)))
1452 RETURN_SERVICE_ERROR(
"Could not register descriptor read callback", service, QLowEnergyService::DescriptorReadError);
1457 auto result =
SAFE(await(characteristic.GetDescriptorsForUuidAsync(GUID(descData.uuid))));
1459 RETURN_SERVICE_ERROR(
"Could not obtain descriptor for uuid", service, QLowEnergyService::DescriptorReadError);
1461 GattCommunicationStatus commStatus;
1462 if (!
TRY(commStatus = result.Status()) || commStatus != GattCommunicationStatus::Success) {
1463 qErrnoWarning(
"Could not obtain list of descriptors");
1464 service->setError(QLowEnergyService::DescriptorReadError);
1468 auto descriptors =
SAFE(result.Descriptors());
1470 RETURN_SERVICE_ERROR(
"Could not obtain descriptor list", service, QLowEnergyService::DescriptorReadError);
1473 if (!
TRY(size = descriptors.Size()))
1474 RETURN_SERVICE_ERROR(
"Could not obtain descriptor list size", service, QLowEnergyService::DescriptorReadError);
1477 qCWarning(QT_BT_WINDOWS) <<
"No descriptor with uuid" << descData.uuid <<
"was found.";
1478 service->setError(QLowEnergyService::DescriptorReadError);
1480 }
else if (size > 1) {
1481 qCWarning(QT_BT_WINDOWS) <<
"There is more than 1 descriptor with uuid" << descData.uuid;
1484 auto descriptor =
SAFE(descriptors.GetAt(0));
1486 RETURN_SERVICE_ERROR(
"Could not obtain descriptor from list", service, QLowEnergyService::DescriptorReadError);
1488 auto readOp =
SAFE(descriptor.ReadValueAsync(BluetoothCacheMode::Uncached));
1490 RETURN_SERVICE_ERROR(
"Could not read descriptor value", service, QLowEnergyService::DescriptorReadError);
1492 auto readCompletedLambda = [charHandle, descHandle, descUuid, service]
1493 (IAsyncOperation<GattReadResult>
const &op, winrt::AsyncStatus
const status)
1495 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1496 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"read operation failed";
1497 service->setError(QLowEnergyService::DescriptorReadError);
1501 auto descriptorValue =
SAFE(op.GetResults());
1502 if (!descriptorValue) {
1503 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain result for descriptor" << descHandle;
1504 service->setError(QLowEnergyService::DescriptorReadError);
1508 descData.uuid = descUuid;
1509 if (descData.uuid == QBluetoothUuid::DescriptorType::CharacteristicUserDescription)
1510 descData.value = byteArrayFromGattResult(descriptorValue,
true);
1512 descData.value = byteArrayFromGattResult(descriptorValue);
1513 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
1514 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
1517 if (!
TRY(readOp.Completed(readCompletedLambda)))
1518 RETURN_SERVICE_ERROR(
"Could not register descriptor read callback", service, QLowEnergyService::DescriptorReadError);
1522 const QSharedPointer<QLowEnergyServicePrivate> service,
1523 const QLowEnergyHandle charHandle,
1524 const QByteArray &newValue,
1525 QLowEnergyService::WriteMode mode)
1527 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << service << charHandle << newValue << mode;
1528 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1529 << QThread::currentThread();
1530 Q_ASSERT(!service.isNull());
1531 if (role == QLowEnergyController::PeripheralRole) {
1532 service->setError(QLowEnergyService::CharacteristicWriteError);
1536 if (!service->characteristicList.contains(charHandle)) {
1537 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle <<
"cannot be found in service"
1539 service->setError(QLowEnergyService::CharacteristicWriteError);
1544 const bool writeWithResponse = mode == QLowEnergyService::WriteWithResponse;
1545 if (!(charData.properties & (writeWithResponse ? QLowEnergyCharacteristic::Write
1546 : QLowEnergyCharacteristic::WriteNoResponse)))
1547 qCDebug(QT_BT_WINDOWS) <<
"Write flag is not set for characteristic" << charHandle;
1549 auto characteristicCallback = [charHandle, service, newValue, writeWithResponse,
this](
1550 GattCharacteristic characteristic) {
1551 writeCharacteristicHelper(service, charHandle, newValue, writeWithResponse,
1555 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1556 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native characteristic" << charData.uuid
1557 <<
"from service" << service->uuid;
1558 service->setError(QLowEnergyService::CharacteristicWriteError);
1562void QLowEnergyControllerPrivateWinRT::writeCharacteristicHelper(
1563 const QSharedPointer<QLowEnergyServicePrivate> service,
1564 const QLowEnergyHandle charHandle,
const QByteArray &newValue,
1565 bool writeWithResponse, GattCharacteristic characteristic)
1567 const quint32 length = quint32(newValue.length());
1568 Buffer buffer =
nullptr;
1569 if (!
TRY(buffer = Buffer(length)))
1570 RETURN_SERVICE_ERROR(
"Could not create buffer", service, QLowEnergyService::CharacteristicWriteError);
1573 if (!
TRY(bytes = buffer.data()))
1574 RETURN_SERVICE_ERROR(
"Could not set buffer", service, QLowEnergyService::CharacteristicWriteError);
1576 memcpy(bytes, newValue, length);
1577 if (!
TRY(buffer.Length(length)))
1578 RETURN_SERVICE_ERROR(
"Could not set buffer length", service, QLowEnergyService::CharacteristicWriteError);
1580 GattWriteOption option = writeWithResponse ? GattWriteOption::WriteWithResponse
1581 : GattWriteOption::WriteWithoutResponse;
1582 auto writeOp =
SAFE(characteristic.WriteValueAsync(buffer, option));
1584 RETURN_SERVICE_ERROR(
"Could not write characteristic", service, QLowEnergyService::CharacteristicWriteError);
1586 const auto charData = service->characteristicList.value(charHandle);
1587 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
1588 auto writeCompletedLambda =
1589 [charData, charHandle, newValue, service, writeWithResponse, thisPtr]
1590 (IAsyncOperation<GattCommunicationStatus>
const &op, winrt::AsyncStatus
const status)
1592 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1593 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle
1594 <<
"write operation failed (async status)";
1595 service->setError(QLowEnergyService::CharacteristicWriteError);
1599 GattCommunicationStatus result;
1600 auto hr =
HR(result = op.GetResults());
1601 if (hr == E_BLUETOOTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH) {
1602 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle
1603 <<
"write operation was tried with invalid value length";
1604 service->setError(QLowEnergyService::CharacteristicWriteError);
1606 }
else if (FAILED(hr)) {
1607 RETURN_SERVICE_ERROR(
"Could not obtain characteristic write result", service, QLowEnergyService::CharacteristicWriteError);
1610 if (result != GattCommunicationStatus::Success) {
1611 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle
1612 <<
"write operation failed (communication status)";
1613 service->setError(QLowEnergyService::CharacteristicWriteError);
1618 if (thisPtr && charData.properties & QLowEnergyCharacteristic::Read)
1619 thisPtr->updateValueOfCharacteristic(charHandle, newValue,
false);
1620 if (writeWithResponse) {
1621 emit service->characteristicWritten(QLowEnergyCharacteristic(service, charHandle),
1626 if (!
TRY(writeOp.Completed(writeCompletedLambda)))
1627 RETURN_SERVICE_ERROR(
"Could not register characteristic write callback", service, QLowEnergyService::CharacteristicWriteError);
1631 const QSharedPointer<QLowEnergyServicePrivate> service,
1632 const QLowEnergyHandle charHandle,
1633 const QLowEnergyHandle descHandle,
1634 const QByteArray &newValue)
1636 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << service << charHandle << descHandle << newValue;
1637 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1638 << QThread::currentThread();
1639 Q_ASSERT(!service.isNull());
1640 if (role == QLowEnergyController::PeripheralRole) {
1641 service->setError(QLowEnergyService::DescriptorWriteError);
1646 if (!service->characteristicList.contains(charHandle)) {
1647 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"in characteristic" << charHandle
1648 <<
"could not be found in service" << service->uuid;
1649 service->setError(QLowEnergyService::DescriptorWriteError);
1653 const auto charData = service->characteristicList.value(charHandle);
1655 auto characteristicCallback = [descHandle, charHandle, service, newValue,
this](
1656 GattCharacteristic characteristic) {
1657 writeDescriptorHelper(service, charHandle, descHandle, newValue, characteristic);
1660 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1661 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native characteristic" << charData.uuid
1662 <<
"from service" << service->uuid;
1663 service->setError(QLowEnergyService::DescriptorWriteError);
1667void QLowEnergyControllerPrivateWinRT::writeDescriptorHelper(
1668 const QSharedPointer<QLowEnergyServicePrivate> service,
1669 const QLowEnergyHandle charHandle,
1670 const QLowEnergyHandle descHandle,
1671 const QByteArray &newValue,
1672 GattCharacteristic characteristic)
1675 const auto charData = service->characteristicList.value(charHandle);
1676 if (!charData.descriptorList.contains(descHandle)) {
1677 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle
1678 <<
"could not be found in Characteristic" << charHandle;
1682 if (descData.uuid ==
1683 QBluetoothUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration)) {
1684 GattClientCharacteristicConfigurationDescriptorValue value;
1685 quint16 intValue = qFromLittleEndian<quint16>(newValue);
1686 if ((intValue & GattClientCharacteristicConfigurationDescriptorValue::Indicate)
1687 && (intValue & GattClientCharacteristicConfigurationDescriptorValue::Notify)) {
1688 qCWarning(QT_BT_WINDOWS) <<
"Setting both Indicate and Notify "
1689 "is not supported on WinRT";
1690 value = GattClientCharacteristicConfigurationDescriptorValue(
1691 (GattClientCharacteristicConfigurationDescriptorValue::Indicate
1692 | GattClientCharacteristicConfigurationDescriptorValue::Notify));
1693 }
else if (intValue & GattClientCharacteristicConfigurationDescriptorValue::Indicate) {
1694 value = GattClientCharacteristicConfigurationDescriptorValue::Indicate;
1695 }
else if (intValue & GattClientCharacteristicConfigurationDescriptorValue::Notify) {
1696 value = GattClientCharacteristicConfigurationDescriptorValue::Notify;
1697 }
else if (intValue == 0) {
1698 value = GattClientCharacteristicConfigurationDescriptorValue::None;
1700 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle
1701 <<
"write operation failed: Invalid value";
1702 service->setError(QLowEnergyService::DescriptorWriteError);
1706 auto writeOp =
SAFE(characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(value));
1708 RETURN_SERVICE_ERROR(
"Could not write client characteristic configuration", service, QLowEnergyService::DescriptorWriteError);
1710 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
1711 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
1712 (IAsyncOperation<GattCommunicationStatus>
const &op, winrt::AsyncStatus
const status)
1714 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1715 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"write operation failed";
1716 service->setError(QLowEnergyService::DescriptorWriteError);
1720 GattCommunicationStatus result;
1721 if (!
TRY(result = op.GetResults()))
1722 RETURN_SERVICE_ERROR(
"Could not obtain result for descriptor", service, QLowEnergyService::DescriptorWriteError);
1724 if (result != GattCommunicationStatus::Success) {
1725 qCWarning(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"write operation failed";
1726 service->setError(QLowEnergyService::DescriptorWriteError);
1730 thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue,
false);
1731 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
1735 if (!
TRY(writeOp.Completed(writeCompletedLambda)))
1736 RETURN_SERVICE_ERROR(
"Could not register descriptor write callback", service, QLowEnergyService::DescriptorWriteError);
1741 auto result =
SAFE(await(characteristic.GetDescriptorsForUuidAsync(GUID(descData.uuid))));
1743 RETURN_SERVICE_ERROR(
"Could not obtain descriptor from Uuid", service, QLowEnergyService::DescriptorWriteError);
1745 GattCommunicationStatus commStatus;
1746 if (!
TRY(commStatus = result.Status()) || commStatus != GattCommunicationStatus::Success) {
1747 qCWarning(QT_BT_WINDOWS) <<
"Descriptor operation failed";
1748 service->setError(QLowEnergyService::DescriptorWriteError);
1752 auto descriptors =
SAFE(result.Descriptors());
1754 RETURN_SERVICE_ERROR(
"Could not obtain list of descriptors", service, QLowEnergyService::DescriptorWriteError);
1757 if (!
TRY(size = descriptors.Size()))
1758 RETURN_SERVICE_ERROR(
"Could not obtain list of descriptors' size", service, QLowEnergyService::DescriptorWriteError);
1761 qCWarning(QT_BT_WINDOWS) <<
"No descriptor with uuid" << descData.uuid <<
"was found.";
1763 }
else if (size > 1) {
1764 qCWarning(QT_BT_WINDOWS) <<
"There is more than 1 descriptor with uuid" << descData.uuid;
1767 auto descriptor =
SAFE(descriptors.GetAt(0));
1769 RETURN_SERVICE_ERROR(
"Could not obtain descriptor", service, QLowEnergyService::DescriptorWriteError);
1771 const quint32 length = quint32(newValue.length());
1772 Buffer buffer =
nullptr;
1773 if (!
TRY(buffer = Buffer(length)))
1774 RETURN_SERVICE_ERROR(
"Could not create buffer", service, QLowEnergyService::CharacteristicWriteError);
1777 if (!
TRY(bytes = buffer.data()))
1778 RETURN_SERVICE_ERROR(
"Could not set buffer", service, QLowEnergyService::CharacteristicWriteError);
1780 memcpy(bytes, newValue, length);
1781 if (!
TRY(buffer.Length(length)))
1782 RETURN_SERVICE_ERROR(
"Could not set buffer length", service, QLowEnergyService::CharacteristicWriteError);
1784 auto writeOp =
SAFE(descriptor.WriteValueAsync(buffer));
1786 RETURN_SERVICE_ERROR(
"Could not write descriptor value", service, QLowEnergyService::CharacteristicWriteError);
1788 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
1789 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
1790 (IAsyncOperation<GattCommunicationStatus>
const &op, winrt::AsyncStatus
const status)
1792 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1793 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"write operation failed";
1794 service->setError(QLowEnergyService::DescriptorWriteError);
1798 GattCommunicationStatus result;
1799 if (!
TRY(result = op.GetResults()))
1800 RETURN_SERVICE_ERROR(
"Could not obtain result for descriptor", service, QLowEnergyService::DescriptorWriteError);
1802 if (result != GattCommunicationStatus::Success) {
1803 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"write operation failed";
1804 service->setError(QLowEnergyService::DescriptorWriteError);
1808 thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue,
false);
1809 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
1813 if (!
TRY(writeOp.Completed(writeCompletedLambda)))
1814 RETURN_SERVICE_ERROR(
"Could not register descriptor write callback", service, QLowEnergyService::DescriptorWriteError);
1824int QLowEnergyControllerPrivateWinRT::
mtu()
const
1827 if (!mGattSession) {
1828 qCDebug(QT_BT_WINDOWS) <<
"mtu queried before GattSession available. Using default mtu.";
1832 if (!
TRY(mtu = mGattSession.MaxPduSize()))
1835 qCDebug(QT_BT_WINDOWS) <<
"mtu determined to be" << mtu;
1839void QLowEnergyControllerPrivateWinRT::handleCharacteristicChanged(
1840 quint16 charHandle,
const QByteArray &data)
1842 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << charHandle << data;
1843 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1844 << QThread::currentThread();
1845 QSharedPointer<QLowEnergyServicePrivate> service =
1846 serviceForHandle(charHandle);
1847 if (service.isNull())
1850 qCDebug(QT_BT_WINDOWS) <<
"Characteristic change notification" << service->uuid
1851 << charHandle << data.toHex();
1853 QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle);
1854 if (!characteristic.isValid()) {
1855 qCWarning(QT_BT_WINDOWS) <<
"characteristicChanged: Cannot find characteristic";
1861 if (characteristic.properties() & QLowEnergyCharacteristic::Read)
1862 updateValueOfCharacteristic(characteristic.attributeHandle(),
1864 emit service->characteristicChanged(characteristic, data);
1867void QLowEnergyControllerPrivateWinRT::handleServiceHandlerError(
const QString &error)
1869 if (state != QLowEnergyController::DiscoveringState)
1872 qCWarning(QT_BT_WINDOWS) <<
"Error while discovering services:" << error;
1873 setState(QLowEnergyController::UnconnectedState);
1874 setError(QLowEnergyController::ConnectionError);
1877void QLowEnergyControllerPrivateWinRT::handleConnectionError(
const char *logMessage)
1879 qCWarning(QT_BT_WINDOWS) << logMessage;
1880 setError(QLowEnergyController::ConnectionError);
1881 setState(QLowEnergyController::UnconnectedState);
1882 unregisterFromStatusChanges();
1883 unregisterFromMtuChanges();
1888#include "qlowenergycontroller_winrt.moc"
void discoverServiceDetails(const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode) override
void stopAdvertising() override
~QLowEnergyControllerPrivateWinRT() override
void addToGenericAttributeList(const QLowEnergyServiceData &service, QLowEnergyHandle startHandle) override
void readDescriptor(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle) override
void connectToDevice() override
void disconnectFromDevice() override
void writeDescriptor(const QSharedPointer< QLowEnergyServicePrivate > service, const QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle, const QByteArray &newValue) override
void startAdvertising(const QLowEnergyAdvertisingParameters ¶ms, 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 ¶ms) override
void discoverServices() override
void invalidateServices()
void setController(QLowEnergyControllerPrivate *control)
void errorOccurred(const QString &error)
void handleDeviceDisconnectRequest()
uint mCharacteristicsCountToBeDiscovered
QHash< QLowEnergyHandle, QLowEnergyServicePrivate::CharData > mCharacteristicList
void errorOccured(const QString &error)
QLowEnergyService::DiscoveryMode mMode
GattDeviceService mDeviceService
QList< QBluetoothUuid > mIndicateChars
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)