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"
763 thisPtr->m_openedServices.insert(serviceUuid, service);
767 qCWarning(QT_BT_WINDOWS) <<
"No services found for Uuid" << serviceUuid;
774bool QLowEnergyControllerPrivateWinRT::getNativeCharacteristic(
775 const QBluetoothUuid &serviceUuid,
const QBluetoothUuid &charUuid,
776 NativeCharacteristicCallback callback)
778 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
779 auto serviceCallback = [thisPtr, callback, charUuid](GattDeviceService service) {
781 auto characteristicRequestOp =
SAFE(service.GetCharacteristicsForUuidAsync(GUID(charUuid)));
782 if (!characteristicRequestOp)
783 RETURN_MSG(
"Could not start async characteristics request");
785 TRY(characteristicRequestOp.Completed(
787 IAsyncOperation<GattCharacteristicsResult>
const &op, winrt::AsyncStatus
const status)
792 if (status != winrt::AsyncStatus::Completed) {
793 qCDebug(QT_BT_WINDOWS) <<
"Failed to get result of async characteristic "
798 auto result =
SAFE(op.GetResults());
800 RETURN_MSG(
"Failed to get result of async characteristic operation");
802 GattCommunicationStatus commStatus;
803 if (!
TRY(commStatus = result.Status()) || commStatus != GattCommunicationStatus::Success) {
804 qErrnoWarning(
"Native characteristic operation failed.");
808 auto characteristics =
SAFE(result.Characteristics());
809 if (!characteristics)
810 RETURN_MSG(
"Could not obtain characteristic list.");
813 if (!
TRY(size = characteristics.Size()))
814 RETURN_MSG(
"Could not obtain characteristic list's size.");
817 qErrnoWarning(
"More than 1 characteristic found.");
819 auto characteristic =
SAFE(characteristics.GetAt(0));
821 RETURN_MSG(
"Could not obtain first characteristic for service");
823 callback(characteristic);
827 if (!getNativeService(serviceUuid, serviceCallback)) {
828 qCDebug(QT_BT_WINDOWS) <<
"Failed to get native service for" << serviceUuid;
835void QLowEnergyControllerPrivateWinRT::registerForValueChanges(
const QBluetoothUuid &serviceUuid,
836 const QBluetoothUuid &charUuid)
838 qCDebug(QT_BT_WINDOWS) <<
"Registering characteristic" << charUuid <<
"in service"
839 << serviceUuid <<
"for value changes";
840 for (
const ValueChangedEntry &entry : std::as_const(mValueChangedTokens)) {
842 if (!
TRY(guuid = entry.characteristic.Uuid()))
844 if (QBluetoothUuid(guuid) == charUuid)
848 auto callback = [
this, charUuid, serviceUuid](GattCharacteristic characteristic) {
849 winrt::event_token token;
850 if (!
TRY(token = characteristic.ValueChanged({
this, &QLowEnergyControllerPrivateWinRT::onValueChange })))
851 RETURN_MSG(
"Could not register characteristic for value changes");
853 mValueChangedTokens.append(ValueChangedEntry(characteristic, token));
854 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charUuid <<
"in service"
855 << serviceUuid <<
"registered for value changes";
858 if (!getNativeCharacteristic(serviceUuid, charUuid, callback)) {
859 qCDebug(QT_BT_WINDOWS).nospace() <<
"Could not obtain native characteristic "
860 << charUuid <<
" from service " << serviceUuid
861 <<
". Qt will not be able to signal"
862 <<
" changes for this characteristic.";
866void QLowEnergyControllerPrivateWinRT::unregisterFromValueChanges()
868 qCDebug(QT_BT_WINDOWS) <<
"Unregistering " << mValueChangedTokens.size() <<
" value change tokens";
869 for (
const ValueChangedEntry &entry : std::as_const(mValueChangedTokens)) {
870 if (!entry.characteristic) {
871 qCWarning(QT_BT_WINDOWS) <<
"Unregistering from value changes for characteristic failed."
872 <<
"Characteristic has been deleted";
875 if (!
TRY(entry.characteristic.ValueChanged(entry.token)))
876 qCWarning(QT_BT_WINDOWS) <<
"Unregistering from value changes for characteristic failed.";
878 mValueChangedTokens.clear();
881void QLowEnergyControllerPrivateWinRT::onValueChange(GattCharacteristic characteristic, GattValueChangedEventArgs
const &args)
884 if (!
TRY(handle = characteristic.AttributeHandle()))
885 RETURN_MSG(
"Could not obtain characteristic's handle");
887 auto buffer =
SAFE(args.CharacteristicValue());
889 RETURN_MSG(
"Could not obtain characteristic's value");
891 emit characteristicChanged(handle, byteArrayFromBuffer(buffer));
894bool QLowEnergyControllerPrivateWinRT::registerForMtuChanges()
896 if (!mDevice || !mGattSession)
898 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
899 if (!
TRY(mMtuChangedToken = mGattSession.MaxPduSizeChanged({
this, &QLowEnergyControllerPrivateWinRT::onMtuChange })))
904void QLowEnergyControllerPrivateWinRT::unregisterFromMtuChanges()
906 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
907 if (mDevice && mGattSession && mMtuChangedToken) {
908 TRY(mGattSession.MaxPduSizeChanged(mMtuChangedToken));
909 mMtuChangedToken = { 0 };
913void QLowEnergyControllerPrivateWinRT::onMtuChange(GattSession session, winrt::IInspectable args)
915 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
917 if (session != mGattSession) {
918 qCWarning(QT_BT_WINDOWS) <<
"Got MTU changed event for wrong or outdated GattSession.";
920 Q_Q(QLowEnergyController);
921 emit q->mtuChanged(mtu());
925bool QLowEnergyControllerPrivateWinRT::registerForStatusChanges()
930 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
932 if (!
TRY(mStatusChangedToken = mDevice.ConnectionStatusChanged({
this, &QLowEnergyControllerPrivateWinRT::onStatusChange })))
937void QLowEnergyControllerPrivateWinRT::unregisterFromStatusChanges()
939 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__;
940 if (mDevice && mStatusChangedToken) {
941 TRY(mDevice.ConnectionStatusChanged(mStatusChangedToken));
942 mStatusChangedToken = { 0 };
946void QLowEnergyControllerPrivateWinRT::onStatusChange(BluetoothLEDevice dev, winrt::IInspectable args)
948 Q_Q(QLowEnergyController);
950 BluetoothConnectionStatus status;
951 if (!
TRY(status = dev.ConnectionStatus()))
952 RETURN_MSG(
"Could not obtain connection status");
954 if (state == QLowEnergyController::ConnectingState
955 && status == BluetoothConnectionStatus::Connected) {
956 setState(QLowEnergyController::ConnectedState);
958 }
else if (state != QLowEnergyController::UnconnectedState
959 && status == BluetoothConnectionStatus::Disconnected) {
961 unregisterFromValueChanges();
962 unregisterFromStatusChanges();
963 unregisterFromMtuChanges();
964 mGattSession =
nullptr;
966 setError(QLowEnergyController::RemoteHostClosedError);
967 setState(QLowEnergyController::UnconnectedState);
968 emit q->disconnected();
972void QLowEnergyControllerPrivateWinRT::obtainIncludedServices(QSharedPointer<QLowEnergyServicePrivate> servicePointer, GattDeviceService service)
974 Q_Q(QLowEnergyController);
976 auto result =
SAFE(await(service.GetIncludedServicesAsync()));
978 RETURN_MSG(
"Could not obtain included services");
981 if (state != QLowEnergyController::DiscoveringState)
984 GattCommunicationStatus status;
985 if (!
TRY(status = result.Status())) {
986 qErrnoWarning(
"Could not obtain list of included services");
990 auto includedServices =
SAFE(result.Services());
991 if (!includedServices)
995 if (!
TRY(count = includedServices.Size()))
996 RETURN_MSG(
"Could not obtain service list's size");
998 for (uint i = 0; i < count; ++i) {
1000 auto includedService =
SAFE(includedServices.GetAt(i));
1001 if (!includedService)
1005 if (!
TRY(guuid = includedService.Uuid()))
1008 const QBluetoothUuid includedUuid(guuid);
1009 QSharedPointer<QLowEnergyServicePrivate> includedPointer;
1010 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__
1011 <<
"Changing service pointer from thread"
1012 << QThread::currentThread();
1013 if (serviceList.contains(includedUuid)) {
1014 includedPointer = serviceList.value(includedUuid);
1017 priv->uuid = includedUuid;
1021 serviceList.insert(includedUuid, includedPointer);
1023 includedPointer->type |= QLowEnergyService::IncludedService;
1024 servicePointer->includedServices.append(includedUuid);
1026 obtainIncludedServices(includedPointer, includedService);
1028 emit q->serviceDiscovered(includedUuid);
1032void QLowEnergyControllerPrivateWinRT::onServiceDiscoveryFinished(IAsyncOperation<GattDeviceServicesResult>
const &op, winrt::AsyncStatus status)
1040 if (state != QLowEnergyController::DiscoveringState)
1043 Q_Q(QLowEnergyController);
1044 if (status != winrt::AsyncStatus::Completed) {
1045 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain services";
1049 auto result =
SAFE(op.GetResults());
1051 return handleConnectionError(
"Could not obtain service discovery result");
1053 GattCommunicationStatus commStatus;
1054 if (!
TRY(commStatus = result.Status()))
1055 return handleConnectionError(
"Could not obtain service discovery status");
1057 if (commStatus != GattCommunicationStatus::Success)
1060 auto deviceServices =
SAFE(result.Services());
1061 if (!deviceServices)
1062 return handleConnectionError(
"Could not obtain service list");
1065 if (!
TRY(serviceCount = deviceServices.Size()))
1066 return handleConnectionError(
"Could not obtain service list size");
1068 for (uint i = 0; i < serviceCount; ++i) {
1070 auto deviceService =
SAFE(deviceServices.GetAt(i));
1075 if (!
TRY(guuid = deviceService.Uuid()))
1078 const QBluetoothUuid service(guuid);
1079 m_openedServices.insert(service, deviceService);
1081 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__
1082 <<
"Changing service pointer from thread"
1083 << QThread::currentThread();
1084 QSharedPointer<QLowEnergyServicePrivate> pointer;
1085 if (serviceList.contains(service)) {
1086 pointer = serviceList.value(service);
1089 priv->uuid = service;
1093 serviceList.insert(service, pointer);
1095 pointer->type |= QLowEnergyService::PrimaryService;
1097 obtainIncludedServices(pointer, deviceService);
1101 if (state != QLowEnergyController::DiscoveringState) {
1102 emit q->discoveryFinished();
1107 emit q->serviceDiscovered(service);
1110 setState(QLowEnergyController::DiscoveredState);
1111 emit q->discoveryFinished();
1114void QLowEnergyControllerPrivateWinRT::clearAllServices()
1118 for (
auto &uuid : m_requestDetailsServiceUuids)
1119 m_openedServices.remove(uuid);
1120 m_requestDetailsServiceUuids.clear();
1122 for (
auto service : m_openedServices)
1123 TRY(service.Close());
1124 m_openedServices.clear();
1127void QLowEnergyControllerPrivateWinRT::closeAndRemoveService(
const QBluetoothUuid &uuid)
1129 auto record = m_openedServices.find(uuid);
1130 if (record != m_openedServices.end()) {
1131 auto service = record.value();
1132 m_openedServices.erase(record);
1134 TRY(service.Close());
1140 qCDebug(QT_BT_WINDOWS) <<
"Service discovery initiated";
1144 auto asyncResult =
SAFE(mDevice.GetGattServicesAsync());
1146 return handleConnectionError(
"Could not obtain services");
1148 if (!
TRY(asyncResult.Completed({
this, &QLowEnergyControllerPrivateWinRT::onServiceDiscoveryFinished })))
1149 return handleConnectionError(
"Could not register services discovery callback");
1153 const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode)
1155 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << service;
1156 if (!serviceList.contains(service)) {
1157 qCWarning(QT_BT_WINDOWS) <<
"Discovery done of unknown service:"
1158 << service.toString();
1163 closeAndRemoveService(service);
1165 auto serviceCallback = [service, mode,
this](GattDeviceService deviceService) {
1166 discoverServiceDetailsHelper(service, mode, deviceService);
1169 if (!getNativeService(service, serviceCallback))
1170 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native service for uuid " << service;
1173void QLowEnergyControllerPrivateWinRT::discoverServiceDetailsHelper(
1174 const QBluetoothUuid &service, QLowEnergyService::DiscoveryMode mode,
1175 GattDeviceService deviceService)
1177 auto reactOnDiscoveryError = [](QSharedPointer<QLowEnergyServicePrivate> service,
1180 qCDebug(QT_BT_WINDOWS) << msg;
1181 service->setError(QLowEnergyService::UnknownError);
1182 service->setState(QLowEnergyService::RemoteService);
1185 QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
1187 qCDebug(QT_BT_WINDOWS) <<
"Device was disconnected while doing service discovery";
1190 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1191 << QThread::currentThread();
1192 pointer->setState(QLowEnergyService::RemoteServiceDiscovering);
1194 auto result =
SAFE(await_forever(deviceService.GetIncludedServicesAsync()));
1196 return reactOnDiscoveryError(pointer,
"Could not obtain included service list");
1198 if (
SAFE(result.Status() != GattCommunicationStatus::Success))
1199 return reactOnDiscoveryError(pointer,
"Obtaining list of included services failed");
1201 auto deviceServices =
SAFE(result.Services());
1202 if (!deviceServices)
1203 return reactOnDiscoveryError(pointer,
"Could not obtain service list from result");
1206 if (!
TRY(serviceCount = deviceServices.Size()))
1207 return reactOnDiscoveryError(pointer,
"Could not obtain included service list's size");
1209 for (uint i = 0; i < serviceCount; ++i) {
1211 auto includedService =
SAFE(deviceServices.GetAt(i));
1212 if (!includedService)
1216 if (!
TRY(guuid = includedService.Uuid()))
1219 const QBluetoothUuid service(guuid);
1220 if (service.isNull()) {
1221 qCDebug(QT_BT_WINDOWS) <<
"Could not find service";
1225 pointer->includedServices.append(service);
1228 QSharedPointer<QLowEnergyServicePrivate> otherService = serviceList.value(service);
1229 if (!otherService.isNull())
1230 otherService->type |= QLowEnergyService::IncludedService;
1235 m_requestDetailsServiceUuids.insert(service);
1236 QThread *thread =
new QThread;
1237 worker->moveToThread(thread);
1238 connect(thread, &QThread::started, worker, &QWinRTLowEnergyServiceHandler::obtainCharList);
1239 connect(thread, &QThread::finished, worker, &QObject::deleteLater);
1240 connect(worker, &QObject::destroyed, thread, &QObject::deleteLater);
1242 worker, &QWinRTLowEnergyServiceHandler::setAbortRequested);
1243 connect(worker, &QWinRTLowEnergyServiceHandler::errorOccured,
1244 this, &QLowEnergyControllerPrivateWinRT::handleServiceHandlerError);
1245 connect(worker, &QWinRTLowEnergyServiceHandler::charListObtained,
this,
1246 [
this](
const QBluetoothUuid &service, QHash<QLowEnergyHandle,
1247 QLowEnergyServicePrivate::CharData> charList, QList<QBluetoothUuid> indicateChars,
1248 QLowEnergyHandle startHandle, QLowEnergyHandle endHandle) {
1249 if (!serviceList.contains(service)) {
1250 qCWarning(QT_BT_WINDOWS)
1251 <<
"Discovery complete for unknown service:" << service.toString();
1254 m_requestDetailsServiceUuids.remove(service);
1256 QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
1257 pointer->startHandle = startHandle;
1258 pointer->endHandle = endHandle;
1259 pointer->characteristicList = charList;
1261 for (
const QBluetoothUuid &indicateChar : std::as_const(indicateChars))
1262 registerForValueChanges(service, indicateChar);
1264 pointer->setState(QLowEnergyService::RemoteServiceDiscovered);
1270 const QLowEnergyAdvertisingParameters &,
1271 const QLowEnergyAdvertisingData &,
1272 const QLowEnergyAdvertisingData &)
1274 setError(QLowEnergyController::AdvertisingError);
1289 const QSharedPointer<QLowEnergyServicePrivate> service,
1290 const QLowEnergyHandle charHandle)
1292 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << service << charHandle;
1293 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1294 << QThread::currentThread();
1295 Q_ASSERT(!service.isNull());
1296 if (role == QLowEnergyController::PeripheralRole) {
1297 service->setError(QLowEnergyService::CharacteristicReadError);
1302 if (!service->characteristicList.contains(charHandle)) {
1303 qCDebug(QT_BT_WINDOWS) << charHandle <<
"could not be found in service" << service->uuid;
1304 service->setError(QLowEnergyService::CharacteristicReadError);
1308 const auto charData = service->characteristicList.value(charHandle);
1309 if (!(charData.properties & QLowEnergyCharacteristic::Read))
1310 qCDebug(QT_BT_WINDOWS) <<
"Read flag is not set for characteristic" << charData.uuid;
1312 auto characteristicCallback = [charHandle, service,
this](
1313 GattCharacteristic characteristic) {
1314 readCharacteristicHelper(service, charHandle, characteristic);
1317 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1318 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native characteristic" << charData.uuid
1319 <<
"from service" << service->uuid;
1320 service->setError(QLowEnergyService::CharacteristicReadError);
1324void QLowEnergyControllerPrivateWinRT::readCharacteristicHelper(
1325 const QSharedPointer<QLowEnergyServicePrivate> service,
1326 const QLowEnergyHandle charHandle,
1327 GattCharacteristic characteristic)
1329 auto readOp =
SAFE(characteristic.ReadValueAsync(BluetoothCacheMode::Uncached));
1331 RETURN_SERVICE_ERROR(
"Could not read characteristic", service, QLowEnergyService::CharacteristicReadError);
1333 auto readCompletedLambda = [charHandle, service]
1334 (IAsyncOperation<GattReadResult>
const &op, winrt::AsyncStatus
const status)
1336 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1337 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle <<
"read operation failed.";
1338 service->setError(QLowEnergyService::CharacteristicReadError);
1341 auto characteristicValue =
SAFE(op.GetResults());
1342 if (!characteristicValue)
1343 RETURN_SERVICE_ERROR(
"Could not obtain result for characteristic", service, QLowEnergyService::CharacteristicReadError);
1345 const QByteArray value = byteArrayFromGattResult(characteristicValue);
1346 auto charData = service->characteristicList.value(charHandle);
1347 charData.value = value;
1348 service->characteristicList.insert(charHandle, charData);
1349 emit service->characteristicRead(QLowEnergyCharacteristic(service, charHandle), value);
1353 if (!
TRY(readOp.Completed(readCompletedLambda)))
1354 RETURN_SERVICE_ERROR(
"Could not register characteristic read callback", service, QLowEnergyService::CharacteristicReadError);
1358 const QSharedPointer<QLowEnergyServicePrivate> service,
1359 const QLowEnergyHandle charHandle,
1360 const QLowEnergyHandle descHandle)
1362 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << service << charHandle << descHandle;
1363 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1364 << QThread::currentThread();
1365 Q_ASSERT(!service.isNull());
1366 if (role == QLowEnergyController::PeripheralRole) {
1367 service->setError(QLowEnergyService::DescriptorReadError);
1372 if (!service->characteristicList.contains(charHandle)) {
1373 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"in characteristic" << charHandle
1374 <<
"cannot be found in service" << service->uuid;
1375 service->setError(QLowEnergyService::DescriptorReadError);
1379 const auto charData = service->characteristicList.value(charHandle);
1381 auto characteristicCallback = [charHandle, descHandle, service,
this](
1382 GattCharacteristic characteristic) {
1383 readDescriptorHelper(service, charHandle, descHandle, characteristic);
1386 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1387 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native characteristic" << charData.uuid
1388 <<
"from service" << service->uuid;
1389 service->setError(QLowEnergyService::DescriptorReadError);
1393void QLowEnergyControllerPrivateWinRT::readDescriptorHelper(
1394 const QSharedPointer<QLowEnergyServicePrivate> service,
1395 const QLowEnergyHandle charHandle,
1396 const QLowEnergyHandle descHandle,
1397 GattCharacteristic characteristic)
1400 const auto charData = service->characteristicList.value(charHandle);
1401 if (!charData.descriptorList.contains(descHandle)) {
1402 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"cannot be found in characteristic"
1405 const auto descData = charData.descriptorList.value(descHandle);
1406 const QBluetoothUuid descUuid = descData.uuid;
1408 QBluetoothUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration)) {
1410 auto readOp =
SAFE(characteristic.ReadClientCharacteristicConfigurationDescriptorAsync());
1412 RETURN_SERVICE_ERROR(
"Could not read client characteristic configuration", service, QLowEnergyService::DescriptorReadError);
1414 auto readCompletedLambda = [charHandle, descHandle, service]
1415 (IAsyncOperation<ClientCharConfigDescriptorResult>
const &op, winrt::AsyncStatus
const status)
1417 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1418 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"read operation failed";
1419 service->setError(QLowEnergyService::DescriptorReadError);
1423 auto iValue =
SAFE(op.GetResults());
1425 RETURN_SERVICE_ERROR(
"Could not obtain result for descriptor", service, QLowEnergyService::DescriptorReadError);
1426 GattClientCharacteristicConfigurationDescriptorValue value;
1427 if (!
TRY(value = iValue.ClientCharacteristicConfigurationDescriptor()))
1428 RETURN_SERVICE_ERROR(
"Could not obtain value for descriptor", service, QLowEnergyService::DescriptorReadError);
1431 bool correct =
false;
1432 if (value & GattClientCharacteristicConfigurationDescriptorValue::Indicate) {
1433 result |= QLowEnergyCharacteristic::Indicate;
1436 if (value & GattClientCharacteristicConfigurationDescriptorValue::Notify) {
1437 result |= QLowEnergyCharacteristic::Notify;
1440 if (value == GattClientCharacteristicConfigurationDescriptorValue::None)
1443 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle
1444 <<
"read operation failed. Obtained unexpected value.";
1445 service->setError(QLowEnergyService::DescriptorReadError);
1449 descData.uuid = QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration;
1450 descData.value = QByteArray(2, Qt::Uninitialized);
1451 qToLittleEndian(result, descData.value.data());
1452 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
1453 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
1458 if (!
TRY(readOp.Completed(readCompletedLambda)))
1459 RETURN_SERVICE_ERROR(
"Could not register descriptor read callback", service, QLowEnergyService::DescriptorReadError);
1464 auto result =
SAFE(await(characteristic.GetDescriptorsForUuidAsync(GUID(descData.uuid))));
1466 RETURN_SERVICE_ERROR(
"Could not obtain descriptor for uuid", service, QLowEnergyService::DescriptorReadError);
1468 GattCommunicationStatus commStatus;
1469 if (!
TRY(commStatus = result.Status()) || commStatus != GattCommunicationStatus::Success) {
1470 qErrnoWarning(
"Could not obtain list of descriptors");
1471 service->setError(QLowEnergyService::DescriptorReadError);
1475 auto descriptors =
SAFE(result.Descriptors());
1477 RETURN_SERVICE_ERROR(
"Could not obtain descriptor list", service, QLowEnergyService::DescriptorReadError);
1480 if (!
TRY(size = descriptors.Size()))
1481 RETURN_SERVICE_ERROR(
"Could not obtain descriptor list size", service, QLowEnergyService::DescriptorReadError);
1484 qCWarning(QT_BT_WINDOWS) <<
"No descriptor with uuid" << descData.uuid <<
"was found.";
1485 service->setError(QLowEnergyService::DescriptorReadError);
1487 }
else if (size > 1) {
1488 qCWarning(QT_BT_WINDOWS) <<
"There is more than 1 descriptor with uuid" << descData.uuid;
1491 auto descriptor =
SAFE(descriptors.GetAt(0));
1493 RETURN_SERVICE_ERROR(
"Could not obtain descriptor from list", service, QLowEnergyService::DescriptorReadError);
1495 auto readOp =
SAFE(descriptor.ReadValueAsync(BluetoothCacheMode::Uncached));
1497 RETURN_SERVICE_ERROR(
"Could not read descriptor value", service, QLowEnergyService::DescriptorReadError);
1499 auto readCompletedLambda = [charHandle, descHandle, descUuid, service]
1500 (IAsyncOperation<GattReadResult>
const &op, winrt::AsyncStatus
const status)
1502 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1503 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"read operation failed";
1504 service->setError(QLowEnergyService::DescriptorReadError);
1508 auto descriptorValue =
SAFE(op.GetResults());
1509 if (!descriptorValue) {
1510 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain result for descriptor" << descHandle;
1511 service->setError(QLowEnergyService::DescriptorReadError);
1515 descData.uuid = descUuid;
1516 if (descData.uuid == QBluetoothUuid::DescriptorType::CharacteristicUserDescription)
1517 descData.value = byteArrayFromGattResult(descriptorValue,
true);
1519 descData.value = byteArrayFromGattResult(descriptorValue);
1520 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
1521 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
1524 if (!
TRY(readOp.Completed(readCompletedLambda)))
1525 RETURN_SERVICE_ERROR(
"Could not register descriptor read callback", service, QLowEnergyService::DescriptorReadError);
1529 const QSharedPointer<QLowEnergyServicePrivate> service,
1530 const QLowEnergyHandle charHandle,
1531 const QByteArray &newValue,
1532 QLowEnergyService::WriteMode mode)
1534 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << service << charHandle << newValue << mode;
1535 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1536 << QThread::currentThread();
1537 Q_ASSERT(!service.isNull());
1538 if (role == QLowEnergyController::PeripheralRole) {
1539 service->setError(QLowEnergyService::CharacteristicWriteError);
1543 if (!service->characteristicList.contains(charHandle)) {
1544 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle <<
"cannot be found in service"
1546 service->setError(QLowEnergyService::CharacteristicWriteError);
1551 const bool writeWithResponse = mode == QLowEnergyService::WriteWithResponse;
1552 if (!(charData.properties & (writeWithResponse ? QLowEnergyCharacteristic::Write
1553 : QLowEnergyCharacteristic::WriteNoResponse)))
1554 qCDebug(QT_BT_WINDOWS) <<
"Write flag is not set for characteristic" << charHandle;
1556 auto characteristicCallback = [charHandle, service, newValue, writeWithResponse,
this](
1557 GattCharacteristic characteristic) {
1558 writeCharacteristicHelper(service, charHandle, newValue, writeWithResponse,
1562 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1563 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native characteristic" << charData.uuid
1564 <<
"from service" << service->uuid;
1565 service->setError(QLowEnergyService::CharacteristicWriteError);
1569void QLowEnergyControllerPrivateWinRT::writeCharacteristicHelper(
1570 const QSharedPointer<QLowEnergyServicePrivate> service,
1571 const QLowEnergyHandle charHandle,
const QByteArray &newValue,
1572 bool writeWithResponse, GattCharacteristic characteristic)
1574 const quint32 length = quint32(newValue.length());
1575 Buffer buffer =
nullptr;
1576 if (!
TRY(buffer = Buffer(length)))
1577 RETURN_SERVICE_ERROR(
"Could not create buffer", service, QLowEnergyService::CharacteristicWriteError);
1580 if (!
TRY(bytes = buffer.data()))
1581 RETURN_SERVICE_ERROR(
"Could not set buffer", service, QLowEnergyService::CharacteristicWriteError);
1583 memcpy(bytes, newValue, length);
1584 if (!
TRY(buffer.Length(length)))
1585 RETURN_SERVICE_ERROR(
"Could not set buffer length", service, QLowEnergyService::CharacteristicWriteError);
1587 GattWriteOption option = writeWithResponse ? GattWriteOption::WriteWithResponse
1588 : GattWriteOption::WriteWithoutResponse;
1589 auto writeOp =
SAFE(characteristic.WriteValueAsync(buffer, option));
1591 RETURN_SERVICE_ERROR(
"Could not write characteristic", service, QLowEnergyService::CharacteristicWriteError);
1593 const auto charData = service->characteristicList.value(charHandle);
1594 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
1595 auto writeCompletedLambda =
1596 [charData, charHandle, newValue, service, writeWithResponse, thisPtr]
1597 (IAsyncOperation<GattCommunicationStatus>
const &op, winrt::AsyncStatus
const status)
1599 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1600 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle
1601 <<
"write operation failed (async status)";
1602 service->setError(QLowEnergyService::CharacteristicWriteError);
1606 GattCommunicationStatus result;
1607 auto hr =
HR(result = op.GetResults());
1608 if (hr == E_BLUETOOTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH) {
1609 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle
1610 <<
"write operation was tried with invalid value length";
1611 service->setError(QLowEnergyService::CharacteristicWriteError);
1613 }
else if (FAILED(hr)) {
1614 RETURN_SERVICE_ERROR(
"Could not obtain characteristic write result", service, QLowEnergyService::CharacteristicWriteError);
1617 if (result != GattCommunicationStatus::Success) {
1618 qCDebug(QT_BT_WINDOWS) <<
"Characteristic" << charHandle
1619 <<
"write operation failed (communication status)";
1620 service->setError(QLowEnergyService::CharacteristicWriteError);
1625 if (thisPtr && charData.properties & QLowEnergyCharacteristic::Read)
1626 thisPtr->updateValueOfCharacteristic(charHandle, newValue,
false);
1627 if (writeWithResponse) {
1628 emit service->characteristicWritten(QLowEnergyCharacteristic(service, charHandle),
1633 if (!
TRY(writeOp.Completed(writeCompletedLambda)))
1634 RETURN_SERVICE_ERROR(
"Could not register characteristic write callback", service, QLowEnergyService::CharacteristicWriteError);
1638 const QSharedPointer<QLowEnergyServicePrivate> service,
1639 const QLowEnergyHandle charHandle,
1640 const QLowEnergyHandle descHandle,
1641 const QByteArray &newValue)
1643 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << service << charHandle << descHandle << newValue;
1644 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1645 << QThread::currentThread();
1646 Q_ASSERT(!service.isNull());
1647 if (role == QLowEnergyController::PeripheralRole) {
1648 service->setError(QLowEnergyService::DescriptorWriteError);
1653 if (!service->characteristicList.contains(charHandle)) {
1654 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"in characteristic" << charHandle
1655 <<
"could not be found in service" << service->uuid;
1656 service->setError(QLowEnergyService::DescriptorWriteError);
1660 const auto charData = service->characteristicList.value(charHandle);
1662 auto characteristicCallback = [descHandle, charHandle, service, newValue,
this](
1663 GattCharacteristic characteristic) {
1664 writeDescriptorHelper(service, charHandle, descHandle, newValue, characteristic);
1667 if (!getNativeCharacteristic(service->uuid, charData.uuid, characteristicCallback)) {
1668 qCDebug(QT_BT_WINDOWS) <<
"Could not obtain native characteristic" << charData.uuid
1669 <<
"from service" << service->uuid;
1670 service->setError(QLowEnergyService::DescriptorWriteError);
1674void QLowEnergyControllerPrivateWinRT::writeDescriptorHelper(
1675 const QSharedPointer<QLowEnergyServicePrivate> service,
1676 const QLowEnergyHandle charHandle,
1677 const QLowEnergyHandle descHandle,
1678 const QByteArray &newValue,
1679 GattCharacteristic characteristic)
1682 const auto charData = service->characteristicList.value(charHandle);
1683 if (!charData.descriptorList.contains(descHandle)) {
1684 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle
1685 <<
"could not be found in Characteristic" << charHandle;
1689 if (descData.uuid ==
1690 QBluetoothUuid(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration)) {
1691 GattClientCharacteristicConfigurationDescriptorValue value;
1692 quint16 intValue = qFromLittleEndian<quint16>(newValue);
1693 if ((intValue & GattClientCharacteristicConfigurationDescriptorValue::Indicate)
1694 && (intValue & GattClientCharacteristicConfigurationDescriptorValue::Notify)) {
1695 qCWarning(QT_BT_WINDOWS) <<
"Setting both Indicate and Notify "
1696 "is not supported on WinRT";
1697 value = GattClientCharacteristicConfigurationDescriptorValue(
1698 (GattClientCharacteristicConfigurationDescriptorValue::Indicate
1699 | GattClientCharacteristicConfigurationDescriptorValue::Notify));
1700 }
else if (intValue & GattClientCharacteristicConfigurationDescriptorValue::Indicate) {
1701 value = GattClientCharacteristicConfigurationDescriptorValue::Indicate;
1702 }
else if (intValue & GattClientCharacteristicConfigurationDescriptorValue::Notify) {
1703 value = GattClientCharacteristicConfigurationDescriptorValue::Notify;
1704 }
else if (intValue == 0) {
1705 value = GattClientCharacteristicConfigurationDescriptorValue::None;
1707 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle
1708 <<
"write operation failed: Invalid value";
1709 service->setError(QLowEnergyService::DescriptorWriteError);
1713 auto writeOp =
SAFE(characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(value));
1715 RETURN_SERVICE_ERROR(
"Could not write client characteristic configuration", service, QLowEnergyService::DescriptorWriteError);
1717 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
1718 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
1719 (IAsyncOperation<GattCommunicationStatus>
const &op, winrt::AsyncStatus
const status)
1721 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1722 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"write operation failed";
1723 service->setError(QLowEnergyService::DescriptorWriteError);
1727 GattCommunicationStatus result;
1728 if (!
TRY(result = op.GetResults()))
1729 RETURN_SERVICE_ERROR(
"Could not obtain result for descriptor", service, QLowEnergyService::DescriptorWriteError);
1731 if (result != GattCommunicationStatus::Success) {
1732 qCWarning(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"write operation failed";
1733 service->setError(QLowEnergyService::DescriptorWriteError);
1737 thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue,
false);
1738 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
1742 if (!
TRY(writeOp.Completed(writeCompletedLambda)))
1743 RETURN_SERVICE_ERROR(
"Could not register descriptor write callback", service, QLowEnergyService::DescriptorWriteError);
1748 auto result =
SAFE(await(characteristic.GetDescriptorsForUuidAsync(GUID(descData.uuid))));
1750 RETURN_SERVICE_ERROR(
"Could not obtain descriptor from Uuid", service, QLowEnergyService::DescriptorWriteError);
1752 GattCommunicationStatus commStatus;
1753 if (!
TRY(commStatus = result.Status()) || commStatus != GattCommunicationStatus::Success) {
1754 qCWarning(QT_BT_WINDOWS) <<
"Descriptor operation failed";
1755 service->setError(QLowEnergyService::DescriptorWriteError);
1759 auto descriptors =
SAFE(result.Descriptors());
1761 RETURN_SERVICE_ERROR(
"Could not obtain list of descriptors", service, QLowEnergyService::DescriptorWriteError);
1764 if (!
TRY(size = descriptors.Size()))
1765 RETURN_SERVICE_ERROR(
"Could not obtain list of descriptors' size", service, QLowEnergyService::DescriptorWriteError);
1768 qCWarning(QT_BT_WINDOWS) <<
"No descriptor with uuid" << descData.uuid <<
"was found.";
1770 }
else if (size > 1) {
1771 qCWarning(QT_BT_WINDOWS) <<
"There is more than 1 descriptor with uuid" << descData.uuid;
1774 auto descriptor =
SAFE(descriptors.GetAt(0));
1776 RETURN_SERVICE_ERROR(
"Could not obtain descriptor", service, QLowEnergyService::DescriptorWriteError);
1778 const quint32 length = quint32(newValue.length());
1779 Buffer buffer =
nullptr;
1780 if (!
TRY(buffer = Buffer(length)))
1781 RETURN_SERVICE_ERROR(
"Could not create buffer", service, QLowEnergyService::CharacteristicWriteError);
1784 if (!
TRY(bytes = buffer.data()))
1785 RETURN_SERVICE_ERROR(
"Could not set buffer", service, QLowEnergyService::CharacteristicWriteError);
1787 memcpy(bytes, newValue, length);
1788 if (!
TRY(buffer.Length(length)))
1789 RETURN_SERVICE_ERROR(
"Could not set buffer length", service, QLowEnergyService::CharacteristicWriteError);
1791 auto writeOp =
SAFE(descriptor.WriteValueAsync(buffer));
1793 RETURN_SERVICE_ERROR(
"Could not write descriptor value", service, QLowEnergyService::CharacteristicWriteError);
1795 QPointer<QLowEnergyControllerPrivateWinRT> thisPtr(
this);
1796 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
1797 (IAsyncOperation<GattCommunicationStatus>
const &op, winrt::AsyncStatus
const status)
1799 if (status == winrt::AsyncStatus::Canceled || status == winrt::AsyncStatus::Error) {
1800 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"write operation failed";
1801 service->setError(QLowEnergyService::DescriptorWriteError);
1805 GattCommunicationStatus result;
1806 if (!
TRY(result = op.GetResults()))
1807 RETURN_SERVICE_ERROR(
"Could not obtain result for descriptor", service, QLowEnergyService::DescriptorWriteError);
1809 if (result != GattCommunicationStatus::Success) {
1810 qCDebug(QT_BT_WINDOWS) <<
"Descriptor" << descHandle <<
"write operation failed";
1811 service->setError(QLowEnergyService::DescriptorWriteError);
1815 thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue,
false);
1816 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
1820 if (!
TRY(writeOp.Completed(writeCompletedLambda)))
1821 RETURN_SERVICE_ERROR(
"Could not register descriptor write callback", service, QLowEnergyService::DescriptorWriteError);
1831int QLowEnergyControllerPrivateWinRT::
mtu()
const
1834 if (!mGattSession) {
1835 qCDebug(QT_BT_WINDOWS) <<
"mtu queried before GattSession available. Using default mtu.";
1839 if (!
TRY(mtu = mGattSession.MaxPduSize()))
1842 qCDebug(QT_BT_WINDOWS) <<
"mtu determined to be" << mtu;
1846void QLowEnergyControllerPrivateWinRT::handleCharacteristicChanged(
1847 quint16 charHandle,
const QByteArray &data)
1849 qCDebug(QT_BT_WINDOWS) <<
__FUNCTION__ << charHandle << data;
1850 qCDebug(QT_BT_WINDOWS_SERVICE_THREAD) <<
__FUNCTION__ <<
"Changing service pointer from thread"
1851 << QThread::currentThread();
1852 QSharedPointer<QLowEnergyServicePrivate> service =
1853 serviceForHandle(charHandle);
1854 if (service.isNull())
1857 qCDebug(QT_BT_WINDOWS) <<
"Characteristic change notification" << service->uuid
1858 << charHandle << data.toHex();
1860 QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle);
1861 if (!characteristic.isValid()) {
1862 qCWarning(QT_BT_WINDOWS) <<
"characteristicChanged: Cannot find characteristic";
1868 if (characteristic.properties() & QLowEnergyCharacteristic::Read)
1869 updateValueOfCharacteristic(characteristic.attributeHandle(),
1871 emit service->characteristicChanged(characteristic, data);
1874void QLowEnergyControllerPrivateWinRT::handleServiceHandlerError(
const QString &error)
1876 if (state != QLowEnergyController::DiscoveringState)
1879 qCWarning(QT_BT_WINDOWS) <<
"Error while discovering services:" << error;
1880 setState(QLowEnergyController::UnconnectedState);
1881 setError(QLowEnergyController::ConnectionError);
1884void QLowEnergyControllerPrivateWinRT::handleConnectionError(
const char *logMessage)
1886 qCWarning(QT_BT_WINDOWS) << logMessage;
1887 setError(QLowEnergyController::ConnectionError);
1888 setState(QLowEnergyController::UnconnectedState);
1889 unregisterFromStatusChanges();
1890 unregisterFromMtuChanges();
1895#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)