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 LOG_HRESULT(hr) qCWarning(QT_BT_WINDOWS) << "HRESULT:" << quint32(hr)
149 } catch (hresult_error const &e) {
153 return hresult{ S_OK } ;
160 } catch (hresult_error const &e) {
171 } catch (hresult_error const &e) {
173 return decltype(x)(0
);
177#define WARN_AND_CONTINUE(msg)
179 qCWarning(QT_BT_WINDOWS) << msg;
183#define DEC_CHAR_COUNT_AND_CONTINUE(msg)
185 qCWarning(QT_BT_WINDOWS) << msg;
186 --mCharacteristicsCountToBeDiscovered;
190#define RETURN_SERVICE_ERROR(msg, service, error)
192 qCDebug(QT_BT_WINDOWS) << msg;
193 service->setError(error);
197#define RETURN_FALSE(msg)
203#define RETURN_MSG(msg)
209Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
210Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS_SERVICE_THREAD)
212static constexpr qint64 kMaxConnectTimeout = 20000;
217 qErrnoWarning(
"nullptr passed to byteArrayFromBuffer");
220 qsizetype size =
SAFE(buffer.Length());
224 QString valueString = QString::fromUtf16(
225 reinterpret_cast<
char16_t *>(buffer.data())).left(size / 2);
226 return valueString.toUtf8();
228 return QByteArray(
reinterpret_cast<
char *>(buffer.data()), size);
232 bool isWCharString =
false)
234 auto buffer =
SAFE(gattResult.Value());
236 qCWarning(QT_BT_WINDOWS) <<
"Could not obtain buffer from GattReadResult";
239 return byteArrayFromBuffer(buffer, isWCharString);
407 void emitErrorAndQuitThread(
const QString &error);
430 if (!mAbortRequested && (mCharacteristicsCountToBeDiscovered == 0)) {
431 emit charListObtained(mService, mCharacteristicList, mIndicateChars,
432 mStartHandle, mEndHandle);
434 QThread::currentThread()->quit();
440 emit errorOccured(error);
441 QThread::currentThread()->quit();
475 void connectToPairedDevice();
476 void connectToUnpairedDevice();
477 void emitErrorAndQuitThread(
const QString &error);
478 void emitErrorAndQuitThread(
const char *error);
479 void emitConnectedAndQuitThread();
481 BluetoothLEDevice mDevice =
nullptr;
482 GattSession mGattSession =
nullptr;
483 const QBluetoothAddress mAddress;
484 bool mAbortConnection =
false;
485 bool mComInitialized =
false;
490 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
491 mComInitialized =
TRY(winrt::init_apartment());
493 auto earlyExit = [
this]() {
return mAbortConnection; };
494 if (!(mDevice =
SAFE(await(BluetoothLEDevice::FromBluetoothAddressAsync(mAddress.toUInt64()), earlyExit))))
495 return emitErrorAndQuitThread(
"Could not find LE device from address");
498 BluetoothDeviceId deviceId =
SAFE(mDevice.BluetoothDeviceId());
500 return emitErrorAndQuitThread(
"Could not get device id");
503 if (!(mGattSession =
SAFE(await(GattSession::FromDeviceIdAsync(deviceId), earlyExit))))
504 return emitErrorAndQuitThread(
"Could not get GattSession from id");
506 BluetoothConnectionStatus status;
507 if (!
TRY(status = mDevice.ConnectionStatus()))
508 return emitErrorAndQuitThread(
"Could not get connection status");
509 if (status == BluetoothConnectionStatus::Connected)
510 return emitConnectedAndQuitThread();
512 QBluetoothLocalDevice localDevice;
513 QBluetoothLocalDevice::Pairing pairing = localDevice.pairingStatus(mAddress);
514 if (pairing == QBluetoothLocalDevice::Unpaired)
515 connectToUnpairedDevice();
517 connectToPairedDevice();
522 mAbortConnection =
true;
527 disconnect(
this, &QWinRTLowEnergyConnectionHandler::deviceConnected,
nullptr,
nullptr);
528 disconnect(
this, &QWinRTLowEnergyConnectionHandler::errorOccurred,
nullptr,
nullptr);
533 auto earlyExit = [
this]() {
return mAbortConnection; };
534 QDeadlineTimer deadline(kMaxConnectTimeout);
535 while (!mAbortConnection && !deadline.hasExpired()) {
537 auto deviceServicesResult =
SAFE(await(mDevice.GetGattServicesAsync(), earlyExit));
538 if (!deviceServicesResult)
539 return emitErrorAndQuitThread(
"Could not obtain services");
541 if (!
SAFE(deviceServicesResult.Status() == GattCommunicationStatus::Success))
542 return emitErrorAndQuitThread(
"Service operation failed");
544 auto deviceServices =
SAFE(deviceServicesResult.Services());
546 return emitErrorAndQuitThread(
"Could not obtain list of services");
549 if (!
TRY(serviceCount = deviceServices.Size()))
550 return emitErrorAndQuitThread(
"Could not obtain size of list of services");
551 if (serviceCount == 0)
552 return emitErrorAndQuitThread(
"Found devices without services");
556 for (uint i = 0; i < serviceCount; ++i) {
558 auto service =
SAFE(deviceServices.GetAt(i));
560 return emitErrorAndQuitThread(
"Could not obtain service");
562 auto characteristicsResult =
SAFE(await(service.GetCharacteristicsAsync(), earlyExit));
563 if (!characteristicsResult)
564 return emitErrorAndQuitThread(
"Could not obtain characteristic");
566 if (!
SAFE(characteristicsResult.Status() == GattCommunicationStatus::Success)) {
567 qCWarning(QT_BT_WINDOWS) <<
"Characteristic operation failed";
571 IVectorView<GattCharacteristic> characteristics =
nullptr;
572 auto hr =
HR(characteristics = characteristicsResult.Characteristics());
573 if (hr == E_ACCESSDENIED) {
576 return emitErrorAndQuitThread(
"Could not obtain characteristic list. "
577 "Please check your manifest capabilities");
578 }
else if (FAILED(hr) || !characteristics) {
579 return emitErrorAndQuitThread(
"Could not obtain characteristic list.");
582 uint characteristicsCount;
583 if (!
TRY(characteristicsCount = characteristics.Size()))
584 return emitErrorAndQuitThread(
"Could not obtain size of characteristic list.");
585 for (uint j = 0; j < characteristicsCount; ++j) {
587 auto characteristic =
SAFE(characteristics.GetAt(j));
589 return emitErrorAndQuitThread(
"Could not get characteristic");
590 GattReadResult result =
nullptr;
591 hr =
HR(result = await(
592 characteristic.ReadValueAsync(BluetoothCacheMode::Uncached), earlyExit));
593 if (hr == E_ILLEGAL_METHOD_CALL) {
597 }
else if (FAILED(hr) || !result) {
598 return emitErrorAndQuitThread(
"Could not read characteristic value");
601 auto buffer =
SAFE(result.Value());
603 qCDebug(QT_BT_WINDOWS) <<
"Problem reading value";
607 emitConnectedAndQuitThread();
615 emitErrorAndQuitThread(
"Connect to device failed due to timeout!");
620 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
622 auto earlyExit = [
this]() {
return mAbortConnection; };
623 QDeadlineTimer deadline(kMaxConnectTimeout);
624 while (!mAbortConnection && !deadline.hasExpired()) {
626 auto deviceServicesResult =
SAFE(await_forever(mDevice.GetGattServicesAsync(), earlyExit));
627 if (!deviceServicesResult)
628 return emitErrorAndQuitThread(
"Could not obtain services");
630 GattCommunicationStatus commStatus;
631 if (!
TRY(commStatus = deviceServicesResult.Status()))
632 return emitErrorAndQuitThread(
"Could not obtain comm status");
633 if (commStatus == GattCommunicationStatus::Unreachable)
635 if (commStatus != GattCommunicationStatus::Success)
636 return emitErrorAndQuitThread(
"Service operation failed");
638 emitConnectedAndQuitThread();
644 emitErrorAndQuitThread(
"Connect to device failed due to timeout!");
649 emit errorOccurred(error);
650 QThread::currentThread()->quit();
655 emitErrorAndQuitThread(QString::fromUtf8(error));
660 emit deviceConnected(mDevice.as<abi::BluetoothLEDevice>(), mGattSession.as<abi::GattSession>());
661 QThread::currentThread()->quit();
664QLowEnergyControllerPrivateWinRT::QLowEnergyControllerPrivateWinRT()
668 connect(
this, &QLowEnergyControllerPrivateWinRT::characteristicChanged,
669 this, &QLowEnergyControllerPrivateWinRT::handleCharacteristicChanged,
670 Qt::QueuedConnection);
675 unregisterFromStatusChanges();
676 unregisterFromValueChanges();
679void QLowEnergyControllerPrivateWinRT::
init()
685 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
686 if (remoteDevice.isNull()) {
687 qCWarning(QT_BT_WINDOWS) <<
"Invalid/null remote device address";
688 setError(QLowEnergyController::UnknownRemoteDeviceError);
691 setState(QLowEnergyController::ConnectingState);
694 QThread *thread =
new QThread;
695 worker->moveToThread(thread);
698 connect(thread, &QThread::started, worker, &QWinRTLowEnergyConnectionHandler::connectToDevice);
699 connect(thread, &QThread::finished, worker, &QObject::deleteLater);
700 connect(worker, &QObject::destroyed, thread, &QObject::deleteLater);
701 connect(worker, &QWinRTLowEnergyConnectionHandler::errorOccurred,
this,
702 [
this](
const QString &msg) { handleConnectionError(msg.toUtf8().constData()); });
703 connect(worker, &QWinRTLowEnergyConnectionHandler::deviceConnected,
this,
704 [
this](com_ptr<abi::BluetoothLEDevice> device, com_ptr<abi::GattSession> session) {
705 if (!device || !session) {
706 handleConnectionError(
"Failed to get device or gatt service");
709 mDevice = device.as<BluetoothLEDevice>();
710 mGattSession = session.as<GattSession>();
712 if (!registerForStatusChanges() || !registerForMtuChanges()) {
713 handleConnectionError(
"Failed to register for changes");
717 Q_Q(QLowEnergyController);
718 setState(QLowEnergyController::ConnectedState);
726 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
727 Q_Q(QLowEnergyController);
728 setState(QLowEnergyController::ClosingState);
729 emit abortConnection();
730 unregisterFromValueChanges();
731 unregisterFromStatusChanges();
732 unregisterFromMtuChanges();
734 mGattSession =
nullptr;
736 setState(QLowEnergyController::UnconnectedState);
737 emit q->disconnected();
740bool QLowEnergyControllerPrivateWinRT::getNativeService(
const QBluetoothUuid &serviceUuid,
741 NativeServiceCallback callback)
743 if (m_openedServices.contains(serviceUuid)) {
744 callback(m_openedServices.value(serviceUuid,
nullptr));
748 auto servicesResultOperation =
SAFE(mDevice.GetGattServicesForUuidAsync(GUID(serviceUuid)));
749 if (!servicesResultOperation)
752 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
753 bool ok =
TRY(servicesResultOperation.Completed(
754 [thisPtr, callback, &serviceUuid](
755 IAsyncOperation<GattDeviceServicesResult>
const &op, winrt::AsyncStatus
const status)
758 qCWarning(QT_BT_WINDOWS) <<
"LE controller was removed while getting native service";
762 if (status != winrt::AsyncStatus::Completed) {
763 qCDebug(QT_BT_WINDOWS) <<
"Failed to get result of async service request";
767 auto result =
SAFE(op.GetResults());
769 RETURN_MSG(
"Failed to get result of async service request");
771 auto services =
SAFE(result.Services());
773 RETURN_MSG(
"Failed to extract services from the result");
775 uint servicesCount = 0;
776 if (!
TRY(servicesCount = services.Size()))
777 RETURN_MSG(
"Failed to extract services count");
779 if (servicesCount > 0) {
780 if (servicesCount > 1) {
781 qWarning() <<
"getNativeService: more than one service detected for UUID"
782 << serviceUuid <<
"The first service will be used.";
785 auto service =
SAFE(services.GetAt(0));
787 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native service for Uuid"
790 thisPtr->m_openedServices.insert(serviceUuid, service);
794 qCWarning(QT_BT_WINDOWS) <<
"No services found for Uuid" << serviceUuid;
801bool QLowEnergyControllerPrivateWinRT::getNativeCharacteristic(
802 const QBluetoothUuid &serviceUuid,
const QBluetoothUuid &charUuid,
803 NativeCharacteristicCallback callback)
805 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
806 auto serviceCallback = [thisPtr, callback, charUuid](GattDeviceService service) {
808 auto characteristicRequestOp =
SAFE(service.GetCharacteristicsForUuidAsync(GUID(charUuid)));
809 if (!characteristicRequestOp)
810 RETURN_MSG(
"Could not start async characteristics request");
812 TRY(characteristicRequestOp.Completed(
814 IAsyncOperation<GattCharacteristicsResult>
const &op, winrt::AsyncStatus
const status)
819 if (status != winrt::AsyncStatus::Completed) {
820 qCDebug(QT_BT_WINDOWS) <<
"Failed to get result of async characteristic "
825 auto result =
SAFE(op.GetResults());
827 RETURN_MSG(
"Failed to get result of async characteristic operation");
829 GattCommunicationStatus commStatus;
830 if (!
TRY(commStatus = result.Status()) || commStatus != GattCommunicationStatus::Success) {
831 qErrnoWarning(
"Native characteristic operation failed.");
835 auto characteristics =
SAFE(result.Characteristics());
836 if (!characteristics)
837 RETURN_MSG(
"Could not obtain characteristic list.");
840 if (!
TRY(size = characteristics.Size()))
841 RETURN_MSG(
"Could not obtain characteristic list's size.");
844 qErrnoWarning(
"More than 1 characteristic found.");
846 auto characteristic =
SAFE(characteristics.GetAt(0));
848 RETURN_MSG(
"Could not obtain first characteristic for service");
850 callback(characteristic);
854 if (!getNativeService(serviceUuid, serviceCallback)) {
855 qCDebug(QT_BT_WINDOWS) <<
"Failed to get native service for" << serviceUuid;
862void QLowEnergyControllerPrivateWinRT::registerForValueChanges(
const QBluetoothUuid &serviceUuid,
863 const QBluetoothUuid &charUuid)
865 qCDebug(QT_BT_WINDOWS) <<
"Registering characteristic" << charUuid <<
"in service"
866 << serviceUuid <<
"for value changes";
867 for (
const ValueChangedEntry &entry : std::as_const(mValueChangedTokens)) {
869 if (!
TRY(guuid = entry.characteristic.Uuid()))
871 if (QBluetoothUuid(guuid) == charUuid)
875 auto callback = [
this, charUuid, serviceUuid](GattCharacteristic characteristic) {
876 winrt::event_token token;
877 if (!
TRY(token = characteristic.ValueChanged({
this, &QLowEnergyControllerPrivateWinRT::onValueChange })))
878 RETURN_MSG(
"Could not register characteristic for value changes");
880 mValueChangedTokens.append(ValueChangedEntry(characteristic, token));
881 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charUuid <<
"in service"
882 << serviceUuid <<
"registered for value changes";
885 if (!getNativeCharacteristic(serviceUuid, charUuid, callback)) {
886 qCDebug(QT_BT_WINDOWS).nospace() <<
"Could not obtain native characteristic "
887 << charUuid <<
" from service " << serviceUuid
888 <<
". Qt will not be able to signal"
889 <<
" changes for this characteristic.";
893void QLowEnergyControllerPrivateWinRT::unregisterFromValueChanges()
895 qCDebug(QT_BT_WINDOWS) <<
"Unregistering " << mValueChangedTokens.size() <<
" value change tokens";
896 for (
const ValueChangedEntry &entry : std::as_const(mValueChangedTokens)) {
897 if (!entry.characteristic) {
898 qCWarning(QT_BT_WINDOWS) <<
"Unregistering from value changes for characteristic failed."
899 <<
"Characteristic has been deleted";
902 if (!
TRY(entry.characteristic.ValueChanged(entry.token)))
903 qCWarning(QT_BT_WINDOWS) <<
"Unregistering from value changes for characteristic failed.";
905 mValueChangedTokens.clear();
908void QLowEnergyControllerPrivateWinRT::onValueChange(GattCharacteristic characteristic, GattValueChangedEventArgs
const &args)
911 if (!
TRY(handle = characteristic.AttributeHandle()))
912 RETURN_MSG(
"Could not obtain characteristic's handle");
914 auto buffer =
SAFE(args.CharacteristicValue());
916 RETURN_MSG(
"Could not obtain characteristic's value");
918 emit characteristicChanged(handle, byteArrayFromBuffer(buffer));
921bool QLowEnergyControllerPrivateWinRT::registerForMtuChanges()
923 if (!mDevice || !mGattSession)
925 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
926 if (!
TRY(mMtuChangedToken = mGattSession.MaxPduSizeChanged({
this, &QLowEnergyControllerPrivateWinRT::onMtuChange })))
931void QLowEnergyControllerPrivateWinRT::unregisterFromMtuChanges()
933 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
934 if (mDevice && mGattSession && mMtuChangedToken) {
935 TRY(mGattSession.MaxPduSizeChanged(mMtuChangedToken));
936 mMtuChangedToken = { 0 };
940void QLowEnergyControllerPrivateWinRT::onMtuChange(GattSession session, winrt::IInspectable args)
942 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
944 if (session != mGattSession) {
945 qCWarning(QT_BT_WINDOWS) <<
"Got MTU changed event for wrong or outdated GattSession.";
947 Q_Q(QLowEnergyController);
948 emit q->mtuChanged(mtu());
952bool QLowEnergyControllerPrivateWinRT::registerForStatusChanges()
957 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
959 if (!
TRY(mStatusChangedToken = mDevice.ConnectionStatusChanged({
this, &QLowEnergyControllerPrivateWinRT::onStatusChange })))
964void QLowEnergyControllerPrivateWinRT::unregisterFromStatusChanges()
966 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
967 if (mDevice && mStatusChangedToken) {
968 TRY(mDevice.ConnectionStatusChanged(mStatusChangedToken));
969 mStatusChangedToken = { 0 };
973void QLowEnergyControllerPrivateWinRT::onStatusChange(BluetoothLEDevice dev, winrt::IInspectable args)
975 Q_Q(QLowEnergyController);
977 BluetoothConnectionStatus status;
978 if (!
TRY(status = dev.ConnectionStatus()))
979 RETURN_MSG(
"Could not obtain connection status");
981 if (state == QLowEnergyController::ConnectingState
982 && status == BluetoothConnectionStatus::Connected) {
983 setState(QLowEnergyController::ConnectedState);
985 }
else if (state != QLowEnergyController::UnconnectedState
986 && status == BluetoothConnectionStatus::Disconnected) {
988 unregisterFromValueChanges();
989 unregisterFromStatusChanges();
990 unregisterFromMtuChanges();
991 mGattSession =
nullptr;
993 setError(QLowEnergyController::RemoteHostClosedError);
994 setState(QLowEnergyController::UnconnectedState);
995 emit q->disconnected();
999void QLowEnergyControllerPrivateWinRT::obtainIncludedServices(QSharedPointer<QLowEnergyServicePrivate> servicePointer, GattDeviceService service)
1001 Q_Q(QLowEnergyController);
1003 auto result =
SAFE(await(service.GetIncludedServicesAsync()));
1005 RETURN_MSG(
"Could not obtain included services");
1008 if (state != QLowEnergyController::DiscoveringState)
1011 GattCommunicationStatus status;
1012 if (!
TRY(status = result.Status())) {
1013 qErrnoWarning(
"Could not obtain list of included services");
1017 auto includedServices =
SAFE(result.Services());
1018 if (!includedServices)
1022 if (!
TRY(count = includedServices.Size()))
1023 RETURN_MSG(
"Could not obtain service list's size");
1025 for (uint i = 0; i < count; ++i) {
1027 auto includedService =
SAFE(includedServices.GetAt(i));
1028 if (!includedService)
1032 if (!
TRY(guuid = includedService.Uuid()))
1035 const QBluetoothUuid includedUuid(guuid);
1036 QSharedPointer<QLowEnergyServicePrivate> includedPointer;
1037 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__
1038 <<
"Changing service pointer from thread"
1039 << QThread::currentThread();
1040 if (serviceList.contains(includedUuid)) {
1041 includedPointer = serviceList.value(includedUuid);
1044 priv->uuid = includedUuid;
1048 serviceList.insert(includedUuid, includedPointer);
1050 includedPointer->type |= QLowEnergyService::IncludedService;
1051 servicePointer->includedServices.append(includedUuid);
1053 obtainIncludedServices(includedPointer, includedService);
1055 emit q->serviceDiscovered(includedUuid);
1059void QLowEnergyControllerPrivateWinRT::onServiceDiscoveryFinished(IAsyncOperation<GattDeviceServicesResult>
const &op, winrt::AsyncStatus status)
1067 if (state != QLowEnergyController::DiscoveringState)
1070 Q_Q(QLowEnergyController);
1071 if (status != winrt::AsyncStatus::Completed) {
1072 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain services";
1076 auto result =
SAFE(op.GetResults());
1078 return handleConnectionError(
"Could not obtain service discovery result");
1080 GattCommunicationStatus commStatus;
1081 if (!
TRY(commStatus = result.Status()))
1082 return handleConnectionError(
"Could not obtain service discovery status");
1084 if (commStatus != GattCommunicationStatus::Success)
1087 auto deviceServices =
SAFE(result.Services());
1088 if (!deviceServices)
1089 return handleConnectionError(
"Could not obtain service list");
1092 if (!
TRY(serviceCount = deviceServices.Size()))
1093 return handleConnectionError(
"Could not obtain service list size");
1095 for (uint i = 0; i < serviceCount; ++i) {
1097 auto deviceService =
SAFE(deviceServices.GetAt(i));
1102 if (!
TRY(guuid = deviceService.Uuid()))
1105 const QBluetoothUuid service(guuid);
1106 m_openedServices.insert(service, deviceService);
1108 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__
1109 <<
"Changing service pointer from thread"
1110 << QThread::currentThread();
1111 QSharedPointer<QLowEnergyServicePrivate> pointer;
1112 if (serviceList.contains(service)) {
1113 pointer = serviceList.value(service);
1116 priv->uuid = service;
1120 serviceList.insert(service, pointer);
1122 pointer->type |= QLowEnergyService::PrimaryService;
1124 obtainIncludedServices(pointer, deviceService);
1128 if (state != QLowEnergyController::DiscoveringState) {
1129 emit q->discoveryFinished();
1134 emit q->serviceDiscovered(service);
1137 setState(QLowEnergyController::DiscoveredState);
1138 emit q->discoveryFinished();
1141void QLowEnergyControllerPrivateWinRT::clearAllServices()
1145 for (
auto &uuid : m_requestDetailsServiceUuids)
1146 m_openedServices.remove(uuid);
1147 m_requestDetailsServiceUuids.clear();
1149 for (
auto service : m_openedServices)
1150 TRY(service.Close());
1151 m_openedServices.clear();
1154void QLowEnergyControllerPrivateWinRT::closeAndRemoveService(
const QBluetoothUuid &uuid)
1156 auto record = m_openedServices.find(uuid);
1157 if (record != m_openedServices.end()) {
1158 auto service = record.value();
1159 m_openedServices.erase(record);
1161 TRY(service.Close());
1167 qCDebug(QT_BT_WINDOWS) <<
"Service discovery initiated";
1171 auto asyncResult =
SAFE(mDevice.GetGattServicesAsync());
1173 return handleConnectionError(
"Could not obtain services");
1175 if (!
TRY(asyncResult.Completed({
this, &QLowEnergyControllerPrivateWinRT::onServiceDiscoveryFinished })))
1176 return handleConnectionError(
"Could not register services discovery callback");
1180 const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode)
1182 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << service;
1183 if (!serviceList.contains(service)) {
1184 qCWarning(QT_BT_WINDOWS) <<
"Discovery done of unknown service:"
1185 << service.toString();
1190 closeAndRemoveService(service);
1192 auto serviceCallback = [service, mode,
this](GattDeviceService deviceService) {
1193 discoverServiceDetailsHelper(service, mode, deviceService);
1196 if (!getNativeService(service, serviceCallback))
1197 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native service for uuid " << service;
1200void QLowEnergyControllerPrivateWinRT::discoverServiceDetailsHelper(
1201 const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode,
1202 GattDeviceService deviceService)
1204 auto reactOnDiscoveryError = [](QSharedPointer<QLowEnergyServicePrivate> service,
1207 qCDebug(QT_BT_WINDOWS) << msg;
1208 service->setError(QLowEnergyService::UnknownError);
1209 service->setState(QLowEnergyService::RemoteService);
1212 QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
1214 qCDebug(QT_BT_WINDOWS) <<
"Device was disconnected while doing service discovery";
1217 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1218 << QThread::currentThread();
1219 pointer->setState(QLowEnergyService::RemoteServiceDiscovering);
1221 auto result =
SAFE(await_forever(deviceService.GetIncludedServicesAsync()));
1223 return reactOnDiscoveryError(pointer,
"Could not obtain included service list");
1225 if (
SAFE(result.Status() != GattCommunicationStatus::Success))
1226 return reactOnDiscoveryError(pointer,
"Obtaining list of included services failed");
1228 auto deviceServices =
SAFE(result.Services());
1229 if (!deviceServices)
1230 return reactOnDiscoveryError(pointer,
"Could not obtain service list from result");
1233 if (!
TRY(serviceCount = deviceServices.Size()))
1234 return reactOnDiscoveryError(pointer,
"Could not obtain included service list's size");
1236 for (uint i = 0; i < serviceCount; ++i) {
1238 auto includedService =
SAFE(deviceServices.GetAt(i));
1239 if (!includedService)
1243 if (!
TRY(guuid = includedService.Uuid()))
1246 const QBluetoothUuid service(guuid);
1247 if (service.isNull()) {
1248 qCDebug(QT_BT_WINDOWS) <<
"Could not find service";
1252 pointer->includedServices.append(service);
1255 QSharedPointer<QLowEnergyServicePrivate> otherService = serviceList.value(service);
1256 if (!otherService.isNull())
1257 otherService->type |= QLowEnergyService::IncludedService;
1262 m_requestDetailsServiceUuids.insert(service);
1263 QThread *thread =
new QThread;
1264 worker->moveToThread(thread);
1265 connect(thread, &QThread::started, worker, &QWinRTLowEnergyServiceHandler::obtainCharList);
1266 connect(thread, &QThread::finished, worker, &QObject::deleteLater);
1267 connect(worker, &QObject::destroyed, thread, &QObject::deleteLater);
1268 connect(
this, &QLowEnergyControllerPrivateWinRT::abortConnection,
1269 worker, &QWinRTLowEnergyServiceHandler::setAbortRequested);
1270 connect(worker, &QWinRTLowEnergyServiceHandler::errorOccured,
1271 this, &QLowEnergyControllerPrivateWinRT::handleServiceHandlerError);
1272 connect(worker, &QWinRTLowEnergyServiceHandler::charListObtained,
this,
1273 [
this](
const QBluetoothUuid &service, QHash<QLowEnergyHandle,
1274 QLowEnergyServicePrivate::CharData> charList, QList<QBluetoothUuid> indicateChars,
1275 QLowEnergyHandle startHandle, QLowEnergyHandle endHandle) {
1276 if (!serviceList.contains(service)) {
1277 qCWarning(QT_BT_WINDOWS)
1278 <<
"Discovery complete for unknown service:" << service.toString();
1281 m_requestDetailsServiceUuids.remove(service);
1283 QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
1284 pointer->startHandle = startHandle;
1285 pointer->endHandle = endHandle;
1286 pointer->characteristicList = charList;
1288 for (
const QBluetoothUuid &indicateChar : std::as_const(indicateChars))
1289 registerForValueChanges(service, indicateChar);
1291 pointer->setState(QLowEnergyService::RemoteServiceDiscovered);
1297 const QLowEnergyAdvertisingParameters &,
1298 const QLowEnergyAdvertisingData &,
1299 const QLowEnergyAdvertisingData &)
1301 setError(QLowEnergyController::AdvertisingError);
1316 const QSharedPointer<QLowEnergyServicePrivate> service,
1317 const QLowEnergyHandle charHandle)
1319 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << service << charHandle;
1320 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1321 << QThread::currentThread();
1322 Q_ASSERT(!service.isNull());
1323 if (role == QLowEnergyController::PeripheralRole) {
1324 service->setError(QLowEnergyService::CharacteristicReadError);
1329 if (!service->characteristicList.contains(charHandle)) {
1330 qCDebug(QT_BT_WINDOWS) << charHandle <<
"could not be found in service" << service->uuid;
1331 service->setError(QLowEnergyService::CharacteristicReadError);
1335 const auto charData = service->characteristicList.value(charHandle);
1336 if (!(charData.properties & QLowEnergyCharacteristic::Read))
1337 qCDebug(QT_BT_WINDOWS) <<
"Read flag is not set for characteristic" << charData.uuid;
1339 auto characteristicCallback = [charHandle, service,
this](
1340 GattCharacteristic characteristic) {
1341 readCharacteristicHelper(service, charHandle, characteristic);
1344 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1345 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native characteristic" << charData.uuid
1346 <<
"from service" << service->uuid;
1347 service->setError(QLowEnergyService::CharacteristicReadError);
1351void QLowEnergyControllerPrivateWinRT::readCharacteristicHelper(
1352 const QSharedPointer<QLowEnergyServicePrivate> service,
1353 const QLowEnergyHandle charHandle,
1354 GattCharacteristic characteristic)
1356 auto readOp =
SAFE(characteristic.ReadValueAsync(BluetoothCacheMode::Uncached));
1358 RETURN_SERVICE_ERROR(
"Could not read characteristic", service, QLowEnergyService::CharacteristicReadError);
1360 auto readCompletedLambda = [charHandle, service]
1361 (IAsyncOperation<GattReadResult>
const &op, winrt::AsyncStatus
const status)
1363 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1364 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle <<
"read operation failed.";
1365 service->setError(QLowEnergyService::CharacteristicReadError);
1368 auto characteristicValue =
SAFE(op.GetResults());
1369 if (!characteristicValue)
1370 RETURN_SERVICE_ERROR(
"Could not obtain result for characteristic", service, QLowEnergyService::CharacteristicReadError);
1372 const QByteArray value = byteArrayFromGattResult(characteristicValue);
1373 auto charData = service->characteristicList.value(charHandle);
1374 charData.value = value;
1375 service->characteristicList.insert(charHandle, charData);
1376 emit service->characteristicRead(QLowEnergyCharacteristic(service, charHandle), value);
1380 if (!
TRY(readOp.Completed(readCompletedLambda)))
1381 RETURN_SERVICE_ERROR(
"Could not register characteristic read callback", service, QLowEnergyService::CharacteristicReadError);
1385 const QSharedPointer<QLowEnergyServicePrivate> service,
1386 const QLowEnergyHandle charHandle,
1387 const QLowEnergyHandle descHandle)
1389 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << service << charHandle << descHandle;
1390 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1391 << QThread::currentThread();
1392 Q_ASSERT(!service.isNull());
1393 if (role == QLowEnergyController::PeripheralRole) {
1394 service->setError(QLowEnergyService::DescriptorReadError);
1399 if (!service->characteristicList.contains(charHandle)) {
1400 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"in characteristic" << charHandle
1401 <<
"cannot be found in service" << service->uuid;
1402 service->setError(QLowEnergyService::DescriptorReadError);
1406 const auto charData = service->characteristicList.value(charHandle);
1408 auto characteristicCallback = [charHandle, descHandle, service,
this](
1409 GattCharacteristic characteristic) {
1410 readDescriptorHelper(service, charHandle, descHandle, characteristic);
1413 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1414 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native characteristic" << charData.uuid
1415 <<
"from service" << service->uuid;
1416 service->setError(QLowEnergyService::DescriptorReadError);
1420void QLowEnergyControllerPrivateWinRT::readDescriptorHelper(
1421 const QSharedPointer<QLowEnergyServicePrivate> service,
1422 const QLowEnergyHandle charHandle,
1423 const QLowEnergyHandle descHandle,
1424 GattCharacteristic characteristic)
1427 const auto charData = service->characteristicList.value(charHandle);
1428 if (!charData.descriptorList.contains(descHandle)) {
1429 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"cannot be found in characteristic"
1432 const auto descData = charData.descriptorList.value(descHandle);
1433 const QBluetoothUuid descUuid = descData.uuid;
1435 QBluetoothUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration)) {
1437 auto readOp =
SAFE(characteristic.ReadClientCharacteristicConfigurationDescriptorAsync());
1439 RETURN_SERVICE_ERROR(
"Could not read client characteristic configuration", service, QLowEnergyService::DescriptorReadError);
1441 auto readCompletedLambda = [charHandle, descHandle, service]
1442 (IAsyncOperation<ClientCharConfigDescriptorResult>
const &op, winrt::AsyncStatus
const status)
1444 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1445 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"read operation failed";
1446 service->setError(QLowEnergyService::DescriptorReadError);
1450 auto iValue =
SAFE(op.GetResults());
1452 RETURN_SERVICE_ERROR(
"Could not obtain result for descriptor", service, QLowEnergyService::DescriptorReadError);
1453 GattClientCharacteristicConfigurationDescriptorValue value;
1454 if (!
TRY(value = iValue.ClientCharacteristicConfigurationDescriptor()))
1455 RETURN_SERVICE_ERROR(
"Could not obtain value for descriptor", service, QLowEnergyService::DescriptorReadError);
1458 bool correct =
false;
1459 if (value & GattClientCharacteristicConfigurationDescriptorValue::Indicate) {
1460 result |= QLowEnergyCharacteristic::Indicate;
1463 if (value & GattClientCharacteristicConfigurationDescriptorValue::Notify) {
1464 result |= QLowEnergyCharacteristic::Notify;
1467 if (value == GattClientCharacteristicConfigurationDescriptorValue::None)
1470 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle
1471 <<
"read operation failed. Obtained unexpected value.";
1472 service->setError(QLowEnergyService::DescriptorReadError);
1476 descData.uuid = QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration;
1477 descData.value = QByteArray(2, Qt::Uninitialized);
1478 qToLittleEndian(result, descData.value.data());
1479 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
1480 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
1485 if (!
TRY(readOp.Completed(readCompletedLambda)))
1486 RETURN_SERVICE_ERROR(
"Could not register descriptor read callback", service, QLowEnergyService::DescriptorReadError);
1491 auto result =
SAFE(await(characteristic.GetDescriptorsForUuidAsync(GUID(descData.uuid))));
1493 RETURN_SERVICE_ERROR(
"Could not obtain descriptor for uuid", service, QLowEnergyService::DescriptorReadError);
1495 GattCommunicationStatus commStatus;
1496 if (!
TRY(commStatus = result.Status()) || commStatus != GattCommunicationStatus::Success) {
1497 qErrnoWarning(
"Could not obtain list of descriptors");
1498 service->setError(QLowEnergyService::DescriptorReadError);
1502 auto descriptors =
SAFE(result.Descriptors());
1504 RETURN_SERVICE_ERROR(
"Could not obtain descriptor list", service, QLowEnergyService::DescriptorReadError);
1507 if (!
TRY(size = descriptors.Size()))
1508 RETURN_SERVICE_ERROR(
"Could not obtain descriptor list size", service, QLowEnergyService::DescriptorReadError);
1511 qCWarning(QT_BT_WINDOWS) <<
"No descriptor with uuid" << descData.uuid <<
"was found.";
1512 service->setError(QLowEnergyService::DescriptorReadError);
1514 }
else if (size > 1) {
1515 qCWarning(QT_BT_WINDOWS) <<
"There is more than 1 descriptor with uuid" << descData.uuid;
1518 auto descriptor =
SAFE(descriptors.GetAt(0));
1520 RETURN_SERVICE_ERROR(
"Could not obtain descriptor from list", service, QLowEnergyService::DescriptorReadError);
1522 auto readOp =
SAFE(descriptor.ReadValueAsync(BluetoothCacheMode::Uncached));
1524 RETURN_SERVICE_ERROR(
"Could not read descriptor value", service, QLowEnergyService::DescriptorReadError);
1526 auto readCompletedLambda = [charHandle, descHandle, descUuid, service]
1527 (IAsyncOperation<GattReadResult>
const &op, winrt::AsyncStatus
const status)
1529 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1530 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"read operation failed";
1531 service->setError(QLowEnergyService::DescriptorReadError);
1535 auto descriptorValue =
SAFE(op.GetResults());
1536 if (!descriptorValue) {
1537 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain result for descriptor" << descHandle;
1538 service->setError(QLowEnergyService::DescriptorReadError);
1542 descData.uuid = descUuid;
1543 if (descData.uuid == QBluetoothUuid::DescriptorType::CharacteristicUserDescription)
1544 descData.value = byteArrayFromGattResult(descriptorValue,
true);
1546 descData.value = byteArrayFromGattResult(descriptorValue);
1547 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
1548 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
1551 if (!
TRY(readOp.Completed(readCompletedLambda)))
1552 RETURN_SERVICE_ERROR(
"Could not register descriptor read callback", service, QLowEnergyService::DescriptorReadError);
1556 const QSharedPointer<QLowEnergyServicePrivate> service,
1557 const QLowEnergyHandle charHandle,
1558 const QByteArray &newValue,
1559 QLowEnergyService::WriteMode mode)
1561 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << service << charHandle << newValue << mode;
1562 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1563 << QThread::currentThread();
1564 Q_ASSERT(!service.isNull());
1565 if (role == QLowEnergyController::PeripheralRole) {
1566 service->setError(QLowEnergyService::CharacteristicWriteError);
1570 if (!service->characteristicList.contains(charHandle)) {
1571 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle <<
"cannot be found in service"
1573 service->setError(QLowEnergyService::CharacteristicWriteError);
1578 const bool writeWithResponse = mode == QLowEnergyService::WriteWithResponse;
1579 if (!(charData.properties & (writeWithResponse ? QLowEnergyCharacteristic::Write
1580 : QLowEnergyCharacteristic::WriteNoResponse)))
1581 qCDebug(QT_BT_WINDOWS) <<
"Write flag is not set for characteristic" << charHandle;
1583 auto characteristicCallback = [charHandle, service, newValue, writeWithResponse,
this](
1584 GattCharacteristic characteristic) {
1585 writeCharacteristicHelper(service, charHandle, newValue, writeWithResponse,
1589 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1590 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native characteristic" << charData.uuid
1591 <<
"from service" << service->uuid;
1592 service->setError(QLowEnergyService::CharacteristicWriteError);
1596void QLowEnergyControllerPrivateWinRT::writeCharacteristicHelper(
1597 const QSharedPointer<QLowEnergyServicePrivate> service,
1598 const QLowEnergyHandle charHandle,
const QByteArray &newValue,
1599 bool writeWithResponse, GattCharacteristic characteristic)
1601 const quint32 length = quint32(newValue.length());
1602 Buffer buffer =
nullptr;
1603 if (!
TRY(buffer = Buffer(length)))
1604 RETURN_SERVICE_ERROR(
"Could not create buffer", service, QLowEnergyService::CharacteristicWriteError);
1607 if (!
TRY(bytes = buffer.data()))
1608 RETURN_SERVICE_ERROR(
"Could not set buffer", service, QLowEnergyService::CharacteristicWriteError);
1610 memcpy(bytes, newValue, length);
1611 if (!
TRY(buffer.Length(length)))
1612 RETURN_SERVICE_ERROR(
"Could not set buffer length", service, QLowEnergyService::CharacteristicWriteError);
1614 GattWriteOption option = writeWithResponse ? GattWriteOption::WriteWithResponse
1615 : GattWriteOption::WriteWithoutResponse;
1616 auto writeOp =
SAFE(characteristic.WriteValueAsync(buffer, option));
1618 RETURN_SERVICE_ERROR(
"Could not write characteristic", service, QLowEnergyService::CharacteristicWriteError);
1620 const auto charData = service->characteristicList.value(charHandle);
1621 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
1622 auto writeCompletedLambda =
1623 [charData, charHandle, newValue, service, writeWithResponse, thisPtr]
1624 (IAsyncOperation<GattCommunicationStatus>
const &op, winrt::AsyncStatus
const status)
1626 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1627 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle
1628 <<
"write operation failed (async status)";
1629 service->setError(QLowEnergyService::CharacteristicWriteError);
1633 GattCommunicationStatus result;
1634 auto hr =
HR(result = op.GetResults());
1635 if (hr == E_BLUETOOTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH) {
1636 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle
1637 <<
"write operation was tried with invalid value length";
1638 service->setError(QLowEnergyService::CharacteristicWriteError);
1640 }
else if (FAILED(hr)) {
1641 RETURN_SERVICE_ERROR(
"Could not obtain characteristic write result", service, QLowEnergyService::CharacteristicWriteError);
1644 if (result != GattCommunicationStatus::Success) {
1645 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle
1646 <<
"write operation failed (communication status)";
1647 service->setError(QLowEnergyService::CharacteristicWriteError);
1652 if (thisPtr && charData.properties & QLowEnergyCharacteristic::Read)
1653 thisPtr->updateValueOfCharacteristic(charHandle, newValue,
false);
1654 if (writeWithResponse) {
1655 emit service->characteristicWritten(QLowEnergyCharacteristic(service, charHandle),
1660 if (!
TRY(writeOp.Completed(writeCompletedLambda)))
1661 RETURN_SERVICE_ERROR(
"Could not register characteristic write callback", service, QLowEnergyService::CharacteristicWriteError);
1665 const QSharedPointer<QLowEnergyServicePrivate> service,
1666 const QLowEnergyHandle charHandle,
1667 const QLowEnergyHandle descHandle,
1668 const QByteArray &newValue)
1670 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << service << charHandle << descHandle << newValue;
1671 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1672 << QThread::currentThread();
1673 Q_ASSERT(!service.isNull());
1674 if (role == QLowEnergyController::PeripheralRole) {
1675 service->setError(QLowEnergyService::DescriptorWriteError);
1680 if (!service->characteristicList.contains(charHandle)) {
1681 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"in characteristic" << charHandle
1682 <<
"could not be found in service" << service->uuid;
1683 service->setError(QLowEnergyService::DescriptorWriteError);
1687 const auto charData = service->characteristicList.value(charHandle);
1689 auto characteristicCallback = [descHandle, charHandle, service, newValue,
this](
1690 GattCharacteristic characteristic) {
1691 writeDescriptorHelper(service, charHandle, descHandle, newValue, characteristic);
1694 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1695 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native characteristic" << charData.uuid
1696 <<
"from service" << service->uuid;
1697 service->setError(QLowEnergyService::DescriptorWriteError);
1701void QLowEnergyControllerPrivateWinRT::writeDescriptorHelper(
1702 const QSharedPointer<QLowEnergyServicePrivate> service,
1703 const QLowEnergyHandle charHandle,
1704 const QLowEnergyHandle descHandle,
1705 const QByteArray &newValue,
1706 GattCharacteristic characteristic)
1709 const auto charData = service->characteristicList.value(charHandle);
1710 if (!charData.descriptorList.contains(descHandle)) {
1711 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle
1712 <<
"could not be found in Characteristic" << charHandle;
1716 if (descData.uuid ==
1717 QBluetoothUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration)) {
1718 GattClientCharacteristicConfigurationDescriptorValue value;
1719 quint16 intValue = qFromLittleEndian<quint16>(newValue);
1720 if ((intValue & GattClientCharacteristicConfigurationDescriptorValue::Indicate)
1721 && (intValue & GattClientCharacteristicConfigurationDescriptorValue::Notify)) {
1722 qCWarning(QT_BT_WINDOWS) <<
"Setting both Indicate and Notify "
1723 "is not supported on WinRT";
1724 value = GattClientCharacteristicConfigurationDescriptorValue(
1725 (GattClientCharacteristicConfigurationDescriptorValue::Indicate
1726 | GattClientCharacteristicConfigurationDescriptorValue::Notify));
1727 }
else if (intValue & GattClientCharacteristicConfigurationDescriptorValue::Indicate) {
1728 value = GattClientCharacteristicConfigurationDescriptorValue::Indicate;
1729 }
else if (intValue & GattClientCharacteristicConfigurationDescriptorValue::Notify) {
1730 value = GattClientCharacteristicConfigurationDescriptorValue::Notify;
1731 }
else if (intValue == 0) {
1732 value = GattClientCharacteristicConfigurationDescriptorValue::None;
1734 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle
1735 <<
"write operation failed: Invalid value";
1736 service->setError(QLowEnergyService::DescriptorWriteError);
1740 auto writeOp =
SAFE(characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(value));
1742 RETURN_SERVICE_ERROR(
"Could not write client characteristic configuration", service, QLowEnergyService::DescriptorWriteError);
1744 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
1745 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
1746 (IAsyncOperation<GattCommunicationStatus>
const &op, winrt::AsyncStatus
const status)
1748 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1749 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"write operation failed";
1750 service->setError(QLowEnergyService::DescriptorWriteError);
1754 GattCommunicationStatus result;
1755 if (!
TRY(result = op.GetResults()))
1756 RETURN_SERVICE_ERROR(
"Could not obtain result for descriptor", service, QLowEnergyService::DescriptorWriteError);
1758 if (result != GattCommunicationStatus::Success) {
1759 qCWarning(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"write operation failed";
1760 service->setError(QLowEnergyService::DescriptorWriteError);
1764 thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue,
false);
1765 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
1769 if (!
TRY(writeOp.Completed(writeCompletedLambda)))
1770 RETURN_SERVICE_ERROR(
"Could not register descriptor write callback", service, QLowEnergyService::DescriptorWriteError);
1775 auto result =
SAFE(await(characteristic.GetDescriptorsForUuidAsync(GUID(descData.uuid))));
1777 RETURN_SERVICE_ERROR(
"Could not obtain descriptor from Uuid", service, QLowEnergyService::DescriptorWriteError);
1779 GattCommunicationStatus commStatus;
1780 if (!
TRY(commStatus = result.Status()) || commStatus != GattCommunicationStatus::Success) {
1781 qCWarning(QT_BT_WINDOWS) <<
"Descriptor operation failed";
1782 service->setError(QLowEnergyService::DescriptorWriteError);
1786 auto descriptors =
SAFE(result.Descriptors());
1788 RETURN_SERVICE_ERROR(
"Could not obtain list of descriptors", service, QLowEnergyService::DescriptorWriteError);
1791 if (!
TRY(size = descriptors.Size()))
1792 RETURN_SERVICE_ERROR(
"Could not obtain list of descriptors' size", service, QLowEnergyService::DescriptorWriteError);
1795 qCWarning(QT_BT_WINDOWS) <<
"No descriptor with uuid" << descData.uuid <<
"was found.";
1797 }
else if (size > 1) {
1798 qCWarning(QT_BT_WINDOWS) <<
"There is more than 1 descriptor with uuid" << descData.uuid;
1801 auto descriptor =
SAFE(descriptors.GetAt(0));
1803 RETURN_SERVICE_ERROR(
"Could not obtain descriptor", service, QLowEnergyService::DescriptorWriteError);
1805 const quint32 length = quint32(newValue.length());
1806 Buffer buffer =
nullptr;
1807 if (!
TRY(buffer = Buffer(length)))
1808 RETURN_SERVICE_ERROR(
"Could not create buffer", service, QLowEnergyService::CharacteristicWriteError);
1811 if (!
TRY(bytes = buffer.data()))
1812 RETURN_SERVICE_ERROR(
"Could not set buffer", service, QLowEnergyService::CharacteristicWriteError);
1814 memcpy(bytes, newValue, length);
1815 if (!
TRY(buffer.Length(length)))
1816 RETURN_SERVICE_ERROR(
"Could not set buffer length", service, QLowEnergyService::CharacteristicWriteError);
1818 auto writeOp =
SAFE(descriptor.WriteValueAsync(buffer));
1820 RETURN_SERVICE_ERROR(
"Could not write descriptor value", service, QLowEnergyService::CharacteristicWriteError);
1822 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
1823 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
1824 (IAsyncOperation<GattCommunicationStatus>
const &op, winrt::AsyncStatus
const status)
1826 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1827 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"write operation failed";
1828 service->setError(QLowEnergyService::DescriptorWriteError);
1832 GattCommunicationStatus result;
1833 if (!
TRY(result = op.GetResults()))
1834 RETURN_SERVICE_ERROR(
"Could not obtain result for descriptor", service, QLowEnergyService::DescriptorWriteError);
1836 if (result != GattCommunicationStatus::Success) {
1837 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"write operation failed";
1838 service->setError(QLowEnergyService::DescriptorWriteError);
1842 thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue,
false);
1843 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
1847 if (!
TRY(writeOp.Completed(writeCompletedLambda)))
1848 RETURN_SERVICE_ERROR(
"Could not register descriptor write callback", service, QLowEnergyService::DescriptorWriteError);
1858int QLowEnergyControllerPrivateWinRT::
mtu()
const
1861 if (!mGattSession) {
1862 qCDebug(QT_BT_WINDOWS) <<
"mtu queried before GattSession available. Using default mtu.";
1866 if (!
TRY(mtu = mGattSession.MaxPduSize()))
1869 qCDebug(QT_BT_WINDOWS) <<
"mtu determined to be" << mtu;
1873void QLowEnergyControllerPrivateWinRT::handleCharacteristicChanged(
1874 quint16 charHandle,
const QByteArray &data)
1876 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << charHandle << data;
1877 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1878 << QThread::currentThread();
1879 QSharedPointer<QLowEnergyServicePrivate> service =
1880 serviceForHandle(charHandle);
1881 if (service.isNull())
1884 qCDebug(QT_BT_WINDOWS) <<
"Characteristic change notification" << service->uuid
1885 << charHandle << data.toHex();
1887 QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle);
1888 if (!characteristic.isValid()) {
1889 qCWarning(QT_BT_WINDOWS) <<
"characteristicChanged: Cannot find characteristic";
1895 if (characteristic.properties() & QLowEnergyCharacteristic::Read)
1896 updateValueOfCharacteristic(characteristic.attributeHandle(),
1898 emit service->characteristicChanged(characteristic, data);
1901void QLowEnergyControllerPrivateWinRT::handleServiceHandlerError(
const QString &error)
1903 if (state != QLowEnergyController::DiscoveringState)
1906 qCWarning(QT_BT_WINDOWS) <<
"Error while discovering services:" << error;
1907 setState(QLowEnergyController::UnconnectedState);
1908 setError(QLowEnergyController::ConnectionError);
1911void QLowEnergyControllerPrivateWinRT::handleConnectionError(
const char *logMessage)
1913 qCWarning(QT_BT_WINDOWS) << logMessage;
1914 setError(QLowEnergyController::ConnectionError);
1915 setState(QLowEnergyController::UnconnectedState);
1916 unregisterFromStatusChanges();
1917 unregisterFromMtuChanges();
1922#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)