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);
380 void emitErrorAndQuitThread(
const QString &error);
403 if (!mAbortRequested && (mCharacteristicsCountToBeDiscovered == 0)) {
404 emit charListObtained(mService, mCharacteristicList, mIndicateChars,
405 mStartHandle, mEndHandle);
407 QThread::currentThread()->quit();
413 emit errorOccured(error);
414 QThread::currentThread()->quit();
448 void connectToPairedDevice();
449 void connectToUnpairedDevice();
450 void emitErrorAndQuitThread(
const QString &error);
451 void emitErrorAndQuitThread(
const char *error);
452 void emitConnectedAndQuitThread();
454 BluetoothLEDevice mDevice =
nullptr;
455 GattSession mGattSession =
nullptr;
456 const QBluetoothAddress mAddress;
457 bool mAbortConnection =
false;
458 bool mComInitialized =
false;
463 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
464 mComInitialized =
TRY(winrt::init_apartment());
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");
471 BluetoothDeviceId deviceId =
SAFE(mDevice.BluetoothDeviceId());
473 return emitErrorAndQuitThread(
"Could not get device id");
476 if (!(mGattSession =
SAFE(await(GattSession::FromDeviceIdAsync(deviceId), earlyExit))))
477 return emitErrorAndQuitThread(
"Could not get GattSession from id");
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();
485 QBluetoothLocalDevice localDevice;
486 QBluetoothLocalDevice::Pairing pairing = localDevice.pairingStatus(mAddress);
487 if (pairing == QBluetoothLocalDevice::Unpaired)
488 connectToUnpairedDevice();
490 connectToPairedDevice();
495 mAbortConnection =
true;
500 disconnect(
this, &QWinRTLowEnergyConnectionHandler::deviceConnected,
nullptr,
nullptr);
501 disconnect(
this, &QWinRTLowEnergyConnectionHandler::errorOccurred,
nullptr,
nullptr);
506 auto earlyExit = [
this]() {
return mAbortConnection; };
507 QDeadlineTimer deadline(kMaxConnectTimeout);
508 while (!mAbortConnection && !deadline.hasExpired()) {
510 auto deviceServicesResult =
SAFE(await(mDevice.GetGattServicesAsync(), earlyExit));
511 if (!deviceServicesResult)
512 return emitErrorAndQuitThread(
"Could not obtain services");
514 if (!
SAFE(deviceServicesResult.Status() == GattCommunicationStatus::Success))
515 return emitErrorAndQuitThread(
"Service operation failed");
517 auto deviceServices =
SAFE(deviceServicesResult.Services());
519 return emitErrorAndQuitThread(
"Could not obtain list of services");
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");
529 for (uint i = 0; i < serviceCount; ++i) {
531 auto service =
SAFE(deviceServices.GetAt(i));
533 return emitErrorAndQuitThread(
"Could not obtain service");
535 auto characteristicsResult =
SAFE(await(service.GetCharacteristicsAsync(), earlyExit));
536 if (!characteristicsResult)
537 return emitErrorAndQuitThread(
"Could not obtain characteristic");
539 if (!
SAFE(characteristicsResult.Status() == GattCommunicationStatus::Success)) {
540 qCWarning(QT_BT_WINDOWS) <<
"Characteristic operation failed";
544 IVectorView<GattCharacteristic> characteristics =
nullptr;
545 auto hr =
HR(characteristics = characteristicsResult.Characteristics());
546 if (hr == E_ACCESSDENIED) {
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.");
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) {
560 auto characteristic =
SAFE(characteristics.GetAt(j));
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) {
570 }
else if (FAILED(hr) || !result) {
571 return emitErrorAndQuitThread(
"Could not read characteristic value");
574 auto buffer =
SAFE(result.Value());
576 qCDebug(QT_BT_WINDOWS) <<
"Problem reading value";
580 emitConnectedAndQuitThread();
588 emitErrorAndQuitThread(
"Connect to device failed due to timeout!");
593 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
595 auto earlyExit = [
this]() {
return mAbortConnection; };
596 QDeadlineTimer deadline(kMaxConnectTimeout);
597 while (!mAbortConnection && !deadline.hasExpired()) {
599 auto deviceServicesResult =
SAFE(await_forever(mDevice.GetGattServicesAsync(), earlyExit));
600 if (!deviceServicesResult)
601 return emitErrorAndQuitThread(
"Could not obtain services");
603 GattCommunicationStatus commStatus;
604 if (!
TRY(commStatus = deviceServicesResult.Status()))
605 return emitErrorAndQuitThread(
"Could not obtain comm status");
606 if (commStatus == GattCommunicationStatus::Unreachable)
608 if (commStatus != GattCommunicationStatus::Success)
609 return emitErrorAndQuitThread(
"Service operation failed");
611 emitConnectedAndQuitThread();
617 emitErrorAndQuitThread(
"Connect to device failed due to timeout!");
622 emit errorOccurred(error);
623 QThread::currentThread()->quit();
628 emitErrorAndQuitThread(QString::fromUtf8(error));
633 emit deviceConnected(mDevice.as<abi::BluetoothLEDevice>(), mGattSession.as<abi::GattSession>());
634 QThread::currentThread()->quit();
637QLowEnergyControllerPrivateWinRT::QLowEnergyControllerPrivateWinRT()
641 connect(
this, &QLowEnergyControllerPrivateWinRT::characteristicChanged,
642 this, &QLowEnergyControllerPrivateWinRT::handleCharacteristicChanged,
643 Qt::QueuedConnection);
648 unregisterFromStatusChanges();
649 unregisterFromValueChanges();
652void QLowEnergyControllerPrivateWinRT::
init()
658 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
659 if (remoteDevice.isNull()) {
660 qCWarning(QT_BT_WINDOWS) <<
"Invalid/null remote device address";
661 setError(QLowEnergyController::UnknownRemoteDeviceError);
664 setState(QLowEnergyController::ConnectingState);
667 QThread *thread =
new QThread;
668 worker->moveToThread(thread);
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");
682 mDevice = device.as<BluetoothLEDevice>();
683 mGattSession = session.as<GattSession>();
685 if (!registerForStatusChanges() || !registerForMtuChanges()) {
686 handleConnectionError(
"Failed to register for changes");
690 Q_Q(QLowEnergyController);
691 setState(QLowEnergyController::ConnectedState);
699 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
700 Q_Q(QLowEnergyController);
701 setState(QLowEnergyController::ClosingState);
702 emit abortConnection();
703 unregisterFromValueChanges();
704 unregisterFromStatusChanges();
705 unregisterFromMtuChanges();
707 mGattSession =
nullptr;
709 setState(QLowEnergyController::UnconnectedState);
710 emit q->disconnected();
713bool QLowEnergyControllerPrivateWinRT::getNativeService(
const QBluetoothUuid &serviceUuid,
714 NativeServiceCallback callback)
716 if (m_openedServices.contains(serviceUuid)) {
717 callback(m_openedServices.value(serviceUuid,
nullptr));
721 auto servicesResultOperation =
SAFE(mDevice.GetGattServicesForUuidAsync(GUID(serviceUuid)));
722 if (!servicesResultOperation)
725 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
726 bool ok =
TRY(servicesResultOperation.Completed(
727 [thisPtr, callback, serviceUuid](
728 IAsyncOperation<GattDeviceServicesResult>
const &op, winrt::AsyncStatus
const status)
731 qCWarning(QT_BT_WINDOWS) <<
"LE controller was removed while getting native service";
735 if (status != winrt::AsyncStatus::Completed) {
736 qCDebug(QT_BT_WINDOWS) <<
"Failed to get result of async service request";
740 auto result =
SAFE(op.GetResults());
742 RETURN_MSG(
"Failed to get result of async service request");
744 auto services =
SAFE(result.Services());
746 RETURN_MSG(
"Failed to extract services from the result");
748 uint servicesCount = 0;
749 if (!
TRY(servicesCount = services.Size()))
750 RETURN_MSG(
"Failed to extract services count");
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.";
758 auto service =
SAFE(services.GetAt(0));
760 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native service for Uuid"
764 QMetaObject::invokeMethod(thisPtr.get(),
765 [thisPtr, callback, service, serviceUuid]() {
766 thisPtr->m_openedServices.insert(serviceUuid, service);
771 qCWarning(QT_BT_WINDOWS) <<
"No services found for Uuid" << serviceUuid;
778bool QLowEnergyControllerPrivateWinRT::getNativeCharacteristic(
779 const QBluetoothUuid &serviceUuid,
const QBluetoothUuid &charUuid,
780 NativeCharacteristicCallback callback)
782 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
783 auto serviceCallback = [thisPtr, callback, charUuid](GattDeviceService service) {
785 auto characteristicRequestOp =
SAFE(service.GetCharacteristicsForUuidAsync(GUID(charUuid)));
786 if (!characteristicRequestOp)
787 RETURN_MSG(
"Could not start async characteristics request");
789 TRY(characteristicRequestOp.Completed(
791 IAsyncOperation<GattCharacteristicsResult>
const &op, winrt::AsyncStatus
const status)
796 if (status != winrt::AsyncStatus::Completed) {
797 qCDebug(QT_BT_WINDOWS) <<
"Failed to get result of async characteristic "
802 auto result =
SAFE(op.GetResults());
804 RETURN_MSG(
"Failed to get result of async characteristic operation");
806 GattCommunicationStatus commStatus;
807 if (!
TRY(commStatus = result.Status()) || commStatus != GattCommunicationStatus::Success) {
808 qErrnoWarning(
"Native characteristic operation failed.");
812 auto characteristics =
SAFE(result.Characteristics());
813 if (!characteristics)
814 RETURN_MSG(
"Could not obtain characteristic list.");
817 if (!
TRY(size = characteristics.Size()))
818 RETURN_MSG(
"Could not obtain characteristic list's size.");
821 qErrnoWarning(
"More than 1 characteristic found.");
823 auto characteristic =
SAFE(characteristics.GetAt(0));
825 RETURN_MSG(
"Could not obtain first characteristic for service");
827 callback(characteristic);
831 if (!getNativeService(serviceUuid, serviceCallback)) {
832 qCDebug(QT_BT_WINDOWS) <<
"Failed to get native service for" << serviceUuid;
839void QLowEnergyControllerPrivateWinRT::registerForValueChanges(
const QBluetoothUuid &serviceUuid,
840 const QBluetoothUuid &charUuid)
842 qCDebug(QT_BT_WINDOWS) <<
"Registering characteristic" << charUuid <<
"in service"
843 << serviceUuid <<
"for value changes";
844 for (
const ValueChangedEntry &entry : std::as_const(mValueChangedTokens)) {
846 if (!
TRY(guuid = entry.characteristic.Uuid()))
848 if (QBluetoothUuid(guuid) == charUuid)
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");
857 mValueChangedTokens.append(ValueChangedEntry(characteristic, token));
858 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charUuid <<
"in service"
859 << serviceUuid <<
"registered for value changes";
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.";
870void QLowEnergyControllerPrivateWinRT::unregisterFromValueChanges()
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";
879 if (!
TRY(entry.characteristic.ValueChanged(entry.token)))
880 qCWarning(QT_BT_WINDOWS) <<
"Unregistering from value changes for characteristic failed.";
882 mValueChangedTokens.clear();
885void QLowEnergyControllerPrivateWinRT::onValueChange(GattCharacteristic characteristic, GattValueChangedEventArgs
const &args)
888 if (!
TRY(handle = characteristic.AttributeHandle()))
889 RETURN_MSG(
"Could not obtain characteristic's handle");
891 auto buffer =
SAFE(args.CharacteristicValue());
893 RETURN_MSG(
"Could not obtain characteristic's value");
895 emit characteristicChanged(handle, byteArrayFromBuffer(buffer));
898bool QLowEnergyControllerPrivateWinRT::registerForMtuChanges()
900 if (!mDevice || !mGattSession)
902 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
903 if (!
TRY(mMtuChangedToken = mGattSession.MaxPduSizeChanged({
this, &QLowEnergyControllerPrivateWinRT::onMtuChange })))
908void QLowEnergyControllerPrivateWinRT::unregisterFromMtuChanges()
910 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
911 if (mDevice && mGattSession && mMtuChangedToken) {
912 TRY(mGattSession.MaxPduSizeChanged(mMtuChangedToken));
913 mMtuChangedToken = { 0 };
917void QLowEnergyControllerPrivateWinRT::onMtuChange(GattSession session, winrt::IInspectable args)
919 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
921 if (session != mGattSession) {
922 qCWarning(QT_BT_WINDOWS) <<
"Got MTU changed event for wrong or outdated GattSession.";
924 Q_Q(QLowEnergyController);
925 emit q->mtuChanged(mtu());
929bool QLowEnergyControllerPrivateWinRT::registerForStatusChanges()
934 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
936 if (!
TRY(mStatusChangedToken = mDevice.ConnectionStatusChanged({
this, &QLowEnergyControllerPrivateWinRT::onStatusChange })))
941void QLowEnergyControllerPrivateWinRT::unregisterFromStatusChanges()
943 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
944 if (mDevice && mStatusChangedToken) {
945 TRY(mDevice.ConnectionStatusChanged(mStatusChangedToken));
946 mStatusChangedToken = { 0 };
950void QLowEnergyControllerPrivateWinRT::onStatusChange(BluetoothLEDevice dev, winrt::IInspectable args)
952 Q_Q(QLowEnergyController);
954 BluetoothConnectionStatus status;
955 if (!
TRY(status = dev.ConnectionStatus()))
956 RETURN_MSG(
"Could not obtain connection status");
958 if (state == QLowEnergyController::ConnectingState
959 && status == BluetoothConnectionStatus::Connected) {
960 setState(QLowEnergyController::ConnectedState);
962 }
else if (state != QLowEnergyController::UnconnectedState
963 && status == BluetoothConnectionStatus::Disconnected) {
965 unregisterFromValueChanges();
966 unregisterFromStatusChanges();
967 unregisterFromMtuChanges();
969 mGattSession =
nullptr;
971 setError(QLowEnergyController::RemoteHostClosedError);
972 setState(QLowEnergyController::UnconnectedState);
973 emit q->disconnected();
977void QLowEnergyControllerPrivateWinRT::obtainIncludedServices(QSharedPointer<QLowEnergyServicePrivate> servicePointer, GattDeviceService service)
979 Q_Q(QLowEnergyController);
981 auto result =
SAFE(await(service.GetIncludedServicesAsync()));
983 RETURN_MSG(
"Could not obtain included services");
986 if (state != QLowEnergyController::DiscoveringState)
989 GattCommunicationStatus status;
990 if (!
TRY(status = result.Status())) {
991 qErrnoWarning(
"Could not obtain list of included services");
995 auto includedServices =
SAFE(result.Services());
996 if (!includedServices)
1000 if (!
TRY(count = includedServices.Size()))
1001 RETURN_MSG(
"Could not obtain service list's size");
1003 for (uint i = 0; i < count; ++i) {
1005 auto includedService =
SAFE(includedServices.GetAt(i));
1006 if (!includedService)
1010 if (!
TRY(guuid = includedService.Uuid()))
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);
1022 priv->uuid = includedUuid;
1026 serviceList.insert(includedUuid, includedPointer);
1028 includedPointer->type |= QLowEnergyService::IncludedService;
1029 servicePointer->includedServices.append(includedUuid);
1031 obtainIncludedServices(includedPointer, includedService);
1033 emit q->serviceDiscovered(includedUuid);
1037void QLowEnergyControllerPrivateWinRT::onServiceDiscoveryFinished(IAsyncOperation<GattDeviceServicesResult>
const &op, winrt::AsyncStatus status)
1045 if (state != QLowEnergyController::DiscoveringState)
1048 Q_Q(QLowEnergyController);
1049 if (status != winrt::AsyncStatus::Completed) {
1050 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain services";
1054 auto result =
SAFE(op.GetResults());
1056 return handleConnectionError(
"Could not obtain service discovery result");
1058 GattCommunicationStatus commStatus;
1059 if (!
TRY(commStatus = result.Status()))
1060 return handleConnectionError(
"Could not obtain service discovery status");
1062 if (commStatus != GattCommunicationStatus::Success)
1065 auto deviceServices =
SAFE(result.Services());
1066 if (!deviceServices)
1067 return handleConnectionError(
"Could not obtain service list");
1070 if (!
TRY(serviceCount = deviceServices.Size()))
1071 return handleConnectionError(
"Could not obtain service list size");
1073 for (uint i = 0; i < serviceCount; ++i) {
1075 auto deviceService =
SAFE(deviceServices.GetAt(i));
1080 if (!
TRY(guuid = deviceService.Uuid()))
1083 const QBluetoothUuid service(guuid);
1084 m_openedServices.insert(service, deviceService);
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);
1094 priv->uuid = service;
1098 serviceList.insert(service, pointer);
1100 pointer->type |= QLowEnergyService::PrimaryService;
1102 obtainIncludedServices(pointer, deviceService);
1106 if (state != QLowEnergyController::DiscoveringState) {
1107 emit q->discoveryFinished();
1112 emit q->serviceDiscovered(service);
1115 setState(QLowEnergyController::DiscoveredState);
1116 emit q->discoveryFinished();
1119void QLowEnergyControllerPrivateWinRT::clearAllServices()
1123 for (
auto &uuid : m_requestDetailsServiceUuids)
1124 m_openedServices.remove(uuid);
1125 m_requestDetailsServiceUuids.clear();
1127 for (
auto service : m_openedServices)
1128 TRY(service.Close());
1129 m_openedServices.clear();
1132void QLowEnergyControllerPrivateWinRT::closeAndRemoveService(
const QBluetoothUuid &uuid)
1134 auto record = m_openedServices.find(uuid);
1135 if (record != m_openedServices.end()) {
1136 auto service = record.value();
1137 m_openedServices.erase(record);
1139 TRY(service.Close());
1145 qCDebug(QT_BT_WINDOWS) <<
"Service discovery initiated";
1149 auto asyncResult =
SAFE(mDevice.GetGattServicesAsync());
1151 return handleConnectionError(
"Could not obtain services");
1153 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
1154 auto discoveryCallback = [thisPtr](IAsyncOperation<GattDeviceServicesResult>
const &op,
1155 winrt::AsyncStatus status) {
1158 QMetaObject::invokeMethod(thisPtr.get(), [thisPtr, op, status]() {
1159 thisPtr->onServiceDiscoveryFinished(op, status);
1163 if (!
TRY(asyncResult.Completed(discoveryCallback)))
1164 return handleConnectionError(
"Could not register services discovery callback");
1168 const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode)
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();
1178 closeAndRemoveService(service);
1180 auto serviceCallback = [service, mode,
this](GattDeviceService deviceService) {
1181 discoverServiceDetailsHelper(service, mode, deviceService);
1184 if (!getNativeService(service, serviceCallback))
1185 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native service for uuid " << service;
1188void QLowEnergyControllerPrivateWinRT::discoverServiceDetailsHelper(
1189 const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode,
1190 GattDeviceService deviceService)
1192 auto reactOnDiscoveryError = [](QSharedPointer<QLowEnergyServicePrivate> service,
1195 qCDebug(QT_BT_WINDOWS) << msg;
1196 service->setError(QLowEnergyService::UnknownError);
1197 service->setState(QLowEnergyService::RemoteService);
1200 QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
1202 qCDebug(QT_BT_WINDOWS) <<
"Device was disconnected while doing service discovery";
1205 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1206 << QThread::currentThread();
1207 pointer->setState(QLowEnergyService::RemoteServiceDiscovering);
1209 auto result =
SAFE(await_forever(deviceService.GetIncludedServicesAsync()));
1211 return reactOnDiscoveryError(pointer,
"Could not obtain included service list");
1213 if (
SAFE(result.Status() != GattCommunicationStatus::Success))
1214 return reactOnDiscoveryError(pointer,
"Obtaining list of included services failed");
1216 auto deviceServices =
SAFE(result.Services());
1217 if (!deviceServices)
1218 return reactOnDiscoveryError(pointer,
"Could not obtain service list from result");
1221 if (!
TRY(serviceCount = deviceServices.Size()))
1222 return reactOnDiscoveryError(pointer,
"Could not obtain included service list's size");
1224 for (uint i = 0; i < serviceCount; ++i) {
1226 auto includedService =
SAFE(deviceServices.GetAt(i));
1227 if (!includedService)
1231 if (!
TRY(guuid = includedService.Uuid()))
1234 const QBluetoothUuid service(guuid);
1235 if (service.isNull()) {
1236 qCDebug(QT_BT_WINDOWS) <<
"Could not find service";
1240 pointer->includedServices.append(service);
1243 QSharedPointer<QLowEnergyServicePrivate> otherService = serviceList.value(service);
1244 if (!otherService.isNull())
1245 otherService->type |= QLowEnergyService::IncludedService;
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);
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();
1269 m_requestDetailsServiceUuids.remove(service);
1271 QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
1272 pointer->startHandle = startHandle;
1273 pointer->endHandle = endHandle;
1274 pointer->characteristicList = charList;
1276 for (
const QBluetoothUuid &indicateChar : std::as_const(indicateChars))
1277 registerForValueChanges(service, indicateChar);
1279 pointer->setState(QLowEnergyService::RemoteServiceDiscovered);
1285 const QLowEnergyAdvertisingParameters &,
1286 const QLowEnergyAdvertisingData &,
1287 const QLowEnergyAdvertisingData &)
1289 setError(QLowEnergyController::AdvertisingError);
1304 const QSharedPointer<QLowEnergyServicePrivate> service,
1305 const QLowEnergyHandle charHandle)
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);
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);
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;
1327 auto characteristicCallback = [charHandle, service,
this](
1328 GattCharacteristic characteristic) {
1329 readCharacteristicHelper(service, charHandle, characteristic);
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);
1339void QLowEnergyControllerPrivateWinRT::readCharacteristicHelper(
1340 const QSharedPointer<QLowEnergyServicePrivate> service,
1341 const QLowEnergyHandle charHandle,
1342 GattCharacteristic characteristic)
1344 auto readOp =
SAFE(characteristic.ReadValueAsync(BluetoothCacheMode::Uncached));
1346 RETURN_SERVICE_ERROR(
"Could not read characteristic", service, QLowEnergyService::CharacteristicReadError);
1348 auto readCompletedLambda = [charHandle, service]
1349 (IAsyncOperation<GattReadResult>
const &op, winrt::AsyncStatus
const status)
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);
1356 auto characteristicValue =
SAFE(op.GetResults());
1357 if (!characteristicValue)
1358 RETURN_SERVICE_ERROR(
"Could not obtain result for characteristic", service, QLowEnergyService::CharacteristicReadError);
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);
1368 if (!
TRY(readOp.Completed(readCompletedLambda)))
1369 RETURN_SERVICE_ERROR(
"Could not register characteristic read callback", service, QLowEnergyService::CharacteristicReadError);
1373 const QSharedPointer<QLowEnergyServicePrivate> service,
1374 const QLowEnergyHandle charHandle,
1375 const QLowEnergyHandle descHandle)
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);
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);
1394 const auto charData = service->characteristicList.value(charHandle);
1396 auto characteristicCallback = [charHandle, descHandle, service,
this](
1397 GattCharacteristic characteristic) {
1398 readDescriptorHelper(service, charHandle, descHandle, characteristic);
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);
1408void QLowEnergyControllerPrivateWinRT::readDescriptorHelper(
1409 const QSharedPointer<QLowEnergyServicePrivate> service,
1410 const QLowEnergyHandle charHandle,
1411 const QLowEnergyHandle descHandle,
1412 GattCharacteristic characteristic)
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"
1420 const auto descData = charData.descriptorList.value(descHandle);
1421 const QBluetoothUuid descUuid = descData.uuid;
1423 QBluetoothUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration)) {
1425 auto readOp =
SAFE(characteristic.ReadClientCharacteristicConfigurationDescriptorAsync());
1427 RETURN_SERVICE_ERROR(
"Could not read client characteristic configuration", service, QLowEnergyService::DescriptorReadError);
1429 auto readCompletedLambda = [charHandle, descHandle, service]
1430 (IAsyncOperation<ClientCharConfigDescriptorResult>
const &op, winrt::AsyncStatus
const status)
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);
1438 auto iValue =
SAFE(op.GetResults());
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);
1446 bool correct =
false;
1447 if (value & GattClientCharacteristicConfigurationDescriptorValue::Indicate) {
1448 result |= QLowEnergyCharacteristic::Indicate;
1451 if (value & GattClientCharacteristicConfigurationDescriptorValue::Notify) {
1452 result |= QLowEnergyCharacteristic::Notify;
1455 if (value == GattClientCharacteristicConfigurationDescriptorValue::None)
1458 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle
1459 <<
"read operation failed. Obtained unexpected value.";
1460 service->setError(QLowEnergyService::DescriptorReadError);
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),
1473 if (!
TRY(readOp.Completed(readCompletedLambda)))
1474 RETURN_SERVICE_ERROR(
"Could not register descriptor read callback", service, QLowEnergyService::DescriptorReadError);
1479 auto result =
SAFE(await(characteristic.GetDescriptorsForUuidAsync(GUID(descData.uuid))));
1481 RETURN_SERVICE_ERROR(
"Could not obtain descriptor for uuid", service, QLowEnergyService::DescriptorReadError);
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);
1490 auto descriptors =
SAFE(result.Descriptors());
1492 RETURN_SERVICE_ERROR(
"Could not obtain descriptor list", service, QLowEnergyService::DescriptorReadError);
1495 if (!
TRY(size = descriptors.Size()))
1496 RETURN_SERVICE_ERROR(
"Could not obtain descriptor list size", service, QLowEnergyService::DescriptorReadError);
1499 qCWarning(QT_BT_WINDOWS) <<
"No descriptor with uuid" << descData.uuid <<
"was found.";
1500 service->setError(QLowEnergyService::DescriptorReadError);
1502 }
else if (size > 1) {
1503 qCWarning(QT_BT_WINDOWS) <<
"There is more than 1 descriptor with uuid" << descData.uuid;
1506 auto descriptor =
SAFE(descriptors.GetAt(0));
1508 RETURN_SERVICE_ERROR(
"Could not obtain descriptor from list", service, QLowEnergyService::DescriptorReadError);
1510 auto readOp =
SAFE(descriptor.ReadValueAsync(BluetoothCacheMode::Uncached));
1512 RETURN_SERVICE_ERROR(
"Could not read descriptor value", service, QLowEnergyService::DescriptorReadError);
1514 auto readCompletedLambda = [charHandle, descHandle, descUuid, service]
1515 (IAsyncOperation<GattReadResult>
const &op, winrt::AsyncStatus
const status)
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);
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);
1530 descData.uuid = descUuid;
1531 if (descData.uuid == QBluetoothUuid::DescriptorType::CharacteristicUserDescription)
1532 descData.value = byteArrayFromGattResult(descriptorValue,
true);
1534 descData.value = byteArrayFromGattResult(descriptorValue);
1535 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
1536 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
1539 if (!
TRY(readOp.Completed(readCompletedLambda)))
1540 RETURN_SERVICE_ERROR(
"Could not register descriptor read callback", service, QLowEnergyService::DescriptorReadError);
1544 const QSharedPointer<QLowEnergyServicePrivate> service,
1545 const QLowEnergyHandle charHandle,
1546 const QByteArray &newValue,
1547 QLowEnergyService::WriteMode mode)
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);
1558 if (!service->characteristicList.contains(charHandle)) {
1559 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle <<
"cannot be found in service"
1561 service->setError(QLowEnergyService::CharacteristicWriteError);
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;
1571 auto characteristicCallback = [charHandle, service, newValue, writeWithResponse,
this](
1572 GattCharacteristic characteristic) {
1573 writeCharacteristicHelper(service, charHandle, newValue, writeWithResponse,
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);
1584void QLowEnergyControllerPrivateWinRT::writeCharacteristicHelper(
1585 const QSharedPointer<QLowEnergyServicePrivate> service,
1586 const QLowEnergyHandle charHandle,
const QByteArray &newValue,
1587 bool writeWithResponse, GattCharacteristic characteristic)
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);
1595 if (!
TRY(bytes = buffer.data()))
1596 RETURN_SERVICE_ERROR(
"Could not set buffer", service, QLowEnergyService::CharacteristicWriteError);
1598 memcpy(bytes, newValue, length);
1599 if (!
TRY(buffer.Length(length)))
1600 RETURN_SERVICE_ERROR(
"Could not set buffer length", service, QLowEnergyService::CharacteristicWriteError);
1602 GattWriteOption option = writeWithResponse ? GattWriteOption::WriteWithResponse
1603 : GattWriteOption::WriteWithoutResponse;
1604 auto writeOp =
SAFE(characteristic.WriteValueAsync(buffer, option));
1606 RETURN_SERVICE_ERROR(
"Could not write characteristic", service, QLowEnergyService::CharacteristicWriteError);
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)
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);
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);
1628 }
else if (FAILED(hr)) {
1629 RETURN_SERVICE_ERROR(
"Could not obtain characteristic write result", service, QLowEnergyService::CharacteristicWriteError);
1632 if (result != GattCommunicationStatus::Success) {
1633 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle
1634 <<
"write operation failed (communication status)";
1635 service->setError(QLowEnergyService::CharacteristicWriteError);
1640 if (thisPtr && charData.properties & QLowEnergyCharacteristic::Read)
1641 thisPtr->updateValueOfCharacteristic(charHandle, newValue,
false);
1642 if (writeWithResponse) {
1643 emit service->characteristicWritten(QLowEnergyCharacteristic(service, charHandle),
1648 if (!
TRY(writeOp.Completed(writeCompletedLambda)))
1649 RETURN_SERVICE_ERROR(
"Could not register characteristic write callback", service, QLowEnergyService::CharacteristicWriteError);
1653 const QSharedPointer<QLowEnergyServicePrivate> service,
1654 const QLowEnergyHandle charHandle,
1655 const QLowEnergyHandle descHandle,
1656 const QByteArray &newValue)
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);
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);
1675 const auto charData = service->characteristicList.value(charHandle);
1677 auto characteristicCallback = [descHandle, charHandle, service, newValue,
this](
1678 GattCharacteristic characteristic) {
1679 writeDescriptorHelper(service, charHandle, descHandle, newValue, characteristic);
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);
1689void QLowEnergyControllerPrivateWinRT::writeDescriptorHelper(
1690 const QSharedPointer<QLowEnergyServicePrivate> service,
1691 const QLowEnergyHandle charHandle,
1692 const QLowEnergyHandle descHandle,
1693 const QByteArray &newValue,
1694 GattCharacteristic characteristic)
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;
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;
1722 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle
1723 <<
"write operation failed: Invalid value";
1724 service->setError(QLowEnergyService::DescriptorWriteError);
1728 auto writeOp =
SAFE(characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(value));
1730 RETURN_SERVICE_ERROR(
"Could not write client characteristic configuration", service, QLowEnergyService::DescriptorWriteError);
1732 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
1733 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
1734 (IAsyncOperation<GattCommunicationStatus>
const &op, winrt::AsyncStatus
const status)
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);
1742 GattCommunicationStatus result;
1743 if (!
TRY(result = op.GetResults()))
1744 RETURN_SERVICE_ERROR(
"Could not obtain result for descriptor", service, QLowEnergyService::DescriptorWriteError);
1746 if (result != GattCommunicationStatus::Success) {
1747 qCWarning(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"write operation failed";
1748 service->setError(QLowEnergyService::DescriptorWriteError);
1752 thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue,
false);
1753 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
1757 if (!
TRY(writeOp.Completed(writeCompletedLambda)))
1758 RETURN_SERVICE_ERROR(
"Could not register descriptor write callback", service, QLowEnergyService::DescriptorWriteError);
1763 auto result =
SAFE(await(characteristic.GetDescriptorsForUuidAsync(GUID(descData.uuid))));
1765 RETURN_SERVICE_ERROR(
"Could not obtain descriptor from Uuid", service, QLowEnergyService::DescriptorWriteError);
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);
1774 auto descriptors =
SAFE(result.Descriptors());
1776 RETURN_SERVICE_ERROR(
"Could not obtain list of descriptors", service, QLowEnergyService::DescriptorWriteError);
1779 if (!
TRY(size = descriptors.Size()))
1780 RETURN_SERVICE_ERROR(
"Could not obtain list of descriptors' size", service, QLowEnergyService::DescriptorWriteError);
1783 qCWarning(QT_BT_WINDOWS) <<
"No descriptor with uuid" << descData.uuid <<
"was found.";
1785 }
else if (size > 1) {
1786 qCWarning(QT_BT_WINDOWS) <<
"There is more than 1 descriptor with uuid" << descData.uuid;
1789 auto descriptor =
SAFE(descriptors.GetAt(0));
1791 RETURN_SERVICE_ERROR(
"Could not obtain descriptor", service, QLowEnergyService::DescriptorWriteError);
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);
1799 if (!
TRY(bytes = buffer.data()))
1800 RETURN_SERVICE_ERROR(
"Could not set buffer", service, QLowEnergyService::CharacteristicWriteError);
1802 memcpy(bytes, newValue, length);
1803 if (!
TRY(buffer.Length(length)))
1804 RETURN_SERVICE_ERROR(
"Could not set buffer length", service, QLowEnergyService::CharacteristicWriteError);
1806 auto writeOp =
SAFE(descriptor.WriteValueAsync(buffer));
1808 RETURN_SERVICE_ERROR(
"Could not write descriptor value", service, QLowEnergyService::CharacteristicWriteError);
1810 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
1811 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
1812 (IAsyncOperation<GattCommunicationStatus>
const &op, winrt::AsyncStatus
const status)
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);
1820 GattCommunicationStatus result;
1821 if (!
TRY(result = op.GetResults()))
1822 RETURN_SERVICE_ERROR(
"Could not obtain result for descriptor", service, QLowEnergyService::DescriptorWriteError);
1824 if (result != GattCommunicationStatus::Success) {
1825 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"write operation failed";
1826 service->setError(QLowEnergyService::DescriptorWriteError);
1830 thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue,
false);
1831 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
1835 if (!
TRY(writeOp.Completed(writeCompletedLambda)))
1836 RETURN_SERVICE_ERROR(
"Could not register descriptor write callback", service, QLowEnergyService::DescriptorWriteError);
1846int QLowEnergyControllerPrivateWinRT::
mtu()
const
1849 if (!mGattSession) {
1850 qCDebug(QT_BT_WINDOWS) <<
"mtu queried before GattSession available. Using default mtu.";
1854 if (!
TRY(mtu = mGattSession.MaxPduSize()))
1857 qCDebug(QT_BT_WINDOWS) <<
"mtu determined to be" << mtu;
1861void QLowEnergyControllerPrivateWinRT::handleCharacteristicChanged(
1862 quint16 charHandle,
const QByteArray &data)
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())
1872 qCDebug(QT_BT_WINDOWS) <<
"Characteristic change notification" << service->uuid
1873 << charHandle << data.toHex();
1875 QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle);
1876 if (!characteristic.isValid()) {
1877 qCWarning(QT_BT_WINDOWS) <<
"characteristicChanged: Cannot find characteristic";
1883 if (characteristic.properties() & QLowEnergyCharacteristic::Read)
1884 updateValueOfCharacteristic(characteristic.attributeHandle(),
1886 emit service->characteristicChanged(characteristic, data);
1889void QLowEnergyControllerPrivateWinRT::handleServiceHandlerError(
const QString &error)
1891 if (state != QLowEnergyController::DiscoveringState)
1894 qCWarning(QT_BT_WINDOWS) <<
"Error while discovering services:" << error;
1895 setState(QLowEnergyController::UnconnectedState);
1896 setError(QLowEnergyController::ConnectionError);
1899void QLowEnergyControllerPrivateWinRT::handleConnectionError(
const char *logMessage)
1901 qCWarning(QT_BT_WINDOWS) << logMessage;
1902 setError(QLowEnergyController::ConnectionError);
1903 setState(QLowEnergyController::UnconnectedState);
1904 unregisterFromStatusChanges();
1905 unregisterFromMtuChanges();
1910#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)