Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtBluetooth module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qlowenergycontroller_winrt_new_p.h"
41#include "qlowenergycontroller_winrt_p.h"
42#include "qbluetoothutils_winrt_p.h"
43
44#include <QtBluetooth/qbluetoothlocaldevice.h>
45#include <QtBluetooth/QLowEnergyCharacteristicData>
46#include <QtBluetooth/QLowEnergyDescriptorData>
47#include <QtBluetooth/private/qbluetoothutils_winrt_p.h>
48
49#ifdef CLASSIC_APP_BUILD
50#define Q_OS_WINRT
51#endif
52#include <QtCore/qfunctions_winrt.h>
53#include <QtCore/QtEndian>
54#include <QtCore/QLoggingCategory>
55#include <private/qeventdispatcher_winrt_p.h>
56
57#include <functional>
58#include <robuffer.h>
59#include <windows.devices.enumeration.h>
60#include <windows.devices.bluetooth.h>
61#include <windows.devices.bluetooth.genericattributeprofile.h>
62#include <windows.foundation.collections.h>
63#include <windows.foundation.metadata.h>
64#include <windows.storage.streams.h>
65
66using namespace Microsoft::WRL;
67using namespace Microsoft::WRL::Wrappers;
68using namespace ABI::Windows::Foundation;
69using namespace ABI::Windows::Foundation::Collections;
70using namespace ABI::Windows::Foundation::Metadata;
71using namespace ABI::Windows::Devices;
72using namespace ABI::Windows::Devices::Bluetooth;
73using namespace ABI::Windows::Devices::Bluetooth::GenericAttributeProfile;
74using namespace ABI::Windows::Devices::Enumeration;
75using namespace ABI::Windows::Storage::Streams;
76
77QT_BEGIN_NAMESPACE
78
79typedef ITypedEventHandler<BluetoothLEDevice *, IInspectable *> StatusHandler;
80typedef ITypedEventHandler<GattCharacteristic *, GattValueChangedEventArgs *> ValueChangedHandler;
81typedef GattReadClientCharacteristicConfigurationDescriptorResult ClientCharConfigDescriptorResult;
82typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharConfigDescriptorResult;
83
84#define EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, ret) \
85 if (FAILED(hr)) { \
86 emitErrorAndQuitThread(hr); \
87 ret; \
88 }
89
90#define WARN_AND_CONTINUE_IF_FAILED(hr, msg) \
91 if (FAILED(hr)) { \
92 qCWarning(QT_BT_WINRT) << msg; \
93 continue; \
94 }
95
96#define CHECK_FOR_DEVICE_CONNECTION_ERROR_IMPL(this, hr, msg, ret) \
97 if (FAILED(hr)) { \
98 qCWarning(QT_BT_WINRT) << msg; \
99 this->unregisterFromStatusChanges(); \
100 this->setError(QLowEnergyController::ConnectionError); \
101 this->setState(QLowEnergyController::UnconnectedState); \
102 ret; \
103 }
104
105#define CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, msg, ret) \
106 CHECK_FOR_DEVICE_CONNECTION_ERROR_IMPL(this, hr, msg, ret)
107
108#define CHECK_HR_AND_SET_SERVICE_ERROR(hr, msg, service, error, ret) \
109 if (FAILED(hr)) { \
110 qCDebug(QT_BT_WINRT) << msg; \
111 service->setError(error); \
112 ret; \
113 }
114
115Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
116Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT_SERVICE_THREAD)
117
118QLowEnergyControllerPrivate *createWinRTLowEnergyController()
119{
120 if (supportsNewLEApi()) {
121 qCDebug(QT_BT_WINRT) << "Using new low energy controller";
122 return new QLowEnergyControllerPrivateWinRTNew();
123 }
124
125 qCDebug(QT_BT_WINRT) << "Using pre 15063 low energy controller";
126 return new QLowEnergyControllerPrivateWinRT();
127}
128
129static QByteArray byteArrayFromGattResult(const ComPtr<IGattReadResult> &gattResult,
130 bool isWCharString = false)
131{
132 ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
133 HRESULT hr;
134 hr = gattResult->get_Value(&buffer);
135 if (FAILED(hr) || !buffer) {
136 qCWarning(QT_BT_WINRT) << "Could not obtain buffer from GattReadResult";
137 return QByteArray();
138 }
139 return byteArrayFromBuffer(buffer, isWCharString);
140}
141
142class QWinRTLowEnergyServiceHandlerNew : public QObject
143{
144 Q_OBJECT
145public:
146 QWinRTLowEnergyServiceHandlerNew(const QBluetoothUuid &service,
147 const ComPtr<IGattDeviceService3> &deviceService)
148 : mService(service)
149 , mDeviceService(deviceService)
150 {
151 qCDebug(QT_BT_WINRT) << __FUNCTION__;
152 }
153
154 ~QWinRTLowEnergyServiceHandlerNew()
155 {
156 }
157
158public slots:
159 void obtainCharList()
160 {
161 mIndicateChars.clear();
162 qCDebug(QT_BT_WINRT) << __FUNCTION__;
163 ComPtr<IAsyncOperation<GattCharacteristicsResult *>> characteristicsOp;
164 ComPtr<IGattCharacteristicsResult> characteristicsResult;
165 HRESULT hr = mDeviceService->GetCharacteristicsAsync(&characteristicsOp);
166 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return);
167 hr = QWinRTFunctions::await(characteristicsOp, characteristicsResult.GetAddressOf(),
168 QWinRTFunctions::ProcessMainThreadEvents, 5000);
169 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return);
170 GattCommunicationStatus status;
171 hr = characteristicsResult->get_Status(&status);
172 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return);
173 if (status != GattCommunicationStatus_Success) {
174 emitErrorAndQuitThread(QLatin1String("Could not obtain char list"));
175 return;
176 }
177 ComPtr<IVectorView<GattCharacteristic *>> characteristics;
178 hr = characteristicsResult->get_Characteristics(&characteristics);
179 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return);
180
181 uint characteristicsCount;
182 hr = characteristics->get_Size(&characteristicsCount);
183 EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return);
184
185 mCharacteristicsCountToBeDiscovered = characteristicsCount;
186 for (uint i = 0; i < characteristicsCount; ++i) {
187 ComPtr<IGattCharacteristic> characteristic;
188 hr = characteristics->GetAt(i, &characteristic);
189 if (FAILED(hr)) {
190 qCWarning(QT_BT_WINRT) << "Could not obtain characteristic at" << i;
191 --mCharacteristicsCountToBeDiscovered;
192 continue;
193 }
194
195 ComPtr<IGattCharacteristic3> characteristic3;
196 hr = characteristic.As(&characteristic3);
197 if (FAILED(hr)) {
198 qCWarning(QT_BT_WINRT) << "Could not cast characteristic";
199 --mCharacteristicsCountToBeDiscovered;
200 continue;
201 }
202
203 // For some strange reason, Windows doesn't discover descriptors of characteristics (if not paired).
204 // Qt API assumes that all characteristics and their descriptors are discovered in one go.
205 // So we start 'GetDescriptorsAsync' for each discovered characteristic and finish only
206 // when GetDescriptorsAsync for all characteristics return.
207 ComPtr<IAsyncOperation<GattDescriptorsResult*>> descAsyncResult;
208 hr = characteristic3->GetDescriptorsAsync(&descAsyncResult);
209 if (FAILED(hr)) {
210 qCWarning(QT_BT_WINRT) << "Could not obtain list of descriptors";
211 --mCharacteristicsCountToBeDiscovered;
212 continue;
213 }
214 hr = descAsyncResult->put_Completed(
215 Callback<IAsyncOperationCompletedHandler<GattDescriptorsResult*>>(
216 [this, characteristic]
217 (IAsyncOperation<GattDescriptorsResult *> *op,
218 AsyncStatus status) {
219 if (status != AsyncStatus::Completed) {
220 qCWarning(QT_BT_WINRT) << "Descriptor operation unsuccessful";
221 --mCharacteristicsCountToBeDiscovered;
222 checkAllCharacteristicsDiscovered();
223 return S_OK;
224 }
225 quint16 handle;
226
227 HRESULT hr = characteristic->get_AttributeHandle(&handle);
228 if (FAILED(hr)) {
229 qCWarning(QT_BT_WINRT) << "Could not obtain characteristic's attribute handle";
230 --mCharacteristicsCountToBeDiscovered;
231 checkAllCharacteristicsDiscovered();
232 return S_OK;
233 }
234 QLowEnergyServicePrivate::CharData charData;
235 charData.valueHandle = handle + 1;
236 if (mStartHandle == 0 || mStartHandle > handle)
237 mStartHandle = handle;
238 if (mEndHandle == 0 || mEndHandle < handle)
239 mEndHandle = handle;
240 GUID guuid;
241 hr = characteristic->get_Uuid(&guuid);
242 if (FAILED(hr)) {
243 qCWarning(QT_BT_WINRT) << "Could not obtain characteristic's Uuid";
244 --mCharacteristicsCountToBeDiscovered;
245 checkAllCharacteristicsDiscovered();
246 return S_OK;
247 }
248 charData.uuid = QBluetoothUuid(guuid);
249 GattCharacteristicProperties properties;
250 hr = characteristic->get_CharacteristicProperties(&properties);
251 if (FAILED(hr)) {
252 qCWarning(QT_BT_WINRT) << "Could not obtain characteristic's properties";
253 --mCharacteristicsCountToBeDiscovered;
254 checkAllCharacteristicsDiscovered();
255 return S_OK;
256 }
257 charData.properties = QLowEnergyCharacteristic::PropertyTypes(properties & 0xff);
258 if (charData.properties & QLowEnergyCharacteristic::Read) {
259 ComPtr<IAsyncOperation<GattReadResult *>> readOp;
260 hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached,
261 &readOp);
262 if (FAILED(hr)) {
263 qCWarning(QT_BT_WINRT) << "Could not read characteristic";
264 --mCharacteristicsCountToBeDiscovered;
265 checkAllCharacteristicsDiscovered();
266 return S_OK;
267 }
268 ComPtr<IGattReadResult> readResult;
269 hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf());
270 if (FAILED(hr)) {
271 qCWarning(QT_BT_WINRT) << "Could not obtain characteristic read result";
272 --mCharacteristicsCountToBeDiscovered;
273 checkAllCharacteristicsDiscovered();
274 return S_OK;
275 }
276 if (!readResult)
277 qCWarning(QT_BT_WINRT) << "Characteristic read result is null";
278 else
279 charData.value = byteArrayFromGattResult(readResult);
280 }
281 mCharacteristicList.insert(handle, charData);
282
283 ComPtr<IVectorView<GattDescriptor *>> descriptors;
284
285 ComPtr<IGattDescriptorsResult> result;
286 hr = op->GetResults(&result);
287 if (FAILED(hr)) {
288 qCWarning(QT_BT_WINRT) << "Could not obtain descriptor read result";
289 --mCharacteristicsCountToBeDiscovered;
290 checkAllCharacteristicsDiscovered();
291 return S_OK;
292 }
293 GattCommunicationStatus commStatus;
294 hr = result->get_Status(&commStatus);
295 if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
296 qCWarning(QT_BT_WINRT) << "Descriptor operation failed";
297 --mCharacteristicsCountToBeDiscovered;
298 checkAllCharacteristicsDiscovered();
299 return S_OK;
300 }
301
302 hr = result->get_Descriptors(&descriptors);
303 if (FAILED(hr)) {
304 qCWarning(QT_BT_WINRT) << "Could not obtain list of descriptors";
305 --mCharacteristicsCountToBeDiscovered;
306 checkAllCharacteristicsDiscovered();
307 return S_OK;
308 }
309
310 uint descriptorCount;
311 hr = descriptors->get_Size(&descriptorCount);
312 if (FAILED(hr)) {
313 qCWarning(QT_BT_WINRT) << "Could not obtain list of descriptors' size";
314 --mCharacteristicsCountToBeDiscovered;
315 checkAllCharacteristicsDiscovered();
316 return S_OK;
317 }
318 for (uint j = 0; j < descriptorCount; ++j) {
319 QLowEnergyServicePrivate::DescData descData;
320 ComPtr<IGattDescriptor> descriptor;
321 hr = descriptors->GetAt(j, &descriptor);
322 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor")
323 quint16 descHandle;
324 hr = descriptor->get_AttributeHandle(&descHandle);
325 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor's attribute handle")
326 GUID descriptorUuid;
327 hr = descriptor->get_Uuid(&descriptorUuid);
328 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor's Uuid")
329 descData.uuid = QBluetoothUuid(descriptorUuid);
330 charData.descriptorList.insert(descHandle, descData);
331 if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) {
332 ComPtr<IAsyncOperation<ClientCharConfigDescriptorResult *>> readOp;
333 hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp);
334 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not read descriptor value")
335 ComPtr<IClientCharConfigDescriptorResult> readResult;
336 hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf());
337 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not await descriptor read result")
338 GattClientCharacteristicConfigurationDescriptorValue value;
339 hr = readResult->get_ClientCharacteristicConfigurationDescriptor(&value);
340 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not get descriptor value from result")
341 quint16 result = 0;
342 bool correct = false;
343 if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) {
344 result |= GattClientCharacteristicConfigurationDescriptorValue_Indicate;
345 correct = true;
346 }
347 if (value & GattClientCharacteristicConfigurationDescriptorValue_Notify) {
348 result |= GattClientCharacteristicConfigurationDescriptorValue_Notify;
349 correct = true;
350 }
351 if (value == GattClientCharacteristicConfigurationDescriptorValue_None) {
352 correct = true;
353 }
354 if (!correct)
355 continue;
356
357 descData.value = QByteArray(2, Qt::Uninitialized);
358 qToLittleEndian(result, descData.value.data());
359 mIndicateChars << charData.uuid;
360 } else {
361 ComPtr<IAsyncOperation<GattReadResult *>> readOp;
362 hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached,
363 &readOp);
364 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not read descriptor value")
365 ComPtr<IGattReadResult> readResult;
366 hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf());
367 WARN_AND_CONTINUE_IF_FAILED(hr, "Could await descriptor read result")
368 if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription)
369 descData.value = byteArrayFromGattResult(readResult, true);
370 else
371 descData.value = byteArrayFromGattResult(readResult);
372 }
373 charData.descriptorList.insert(descHandle, descData);
374 }
375
376 mCharacteristicList.insert(handle, charData);
377 --mCharacteristicsCountToBeDiscovered;
378 checkAllCharacteristicsDiscovered();
379 return S_OK;
380 }).Get());
381 if (FAILED(hr)) {
382 qCWarning(QT_BT_WINRT) << "Could not register descriptor callback";
383 --mCharacteristicsCountToBeDiscovered;
384 continue;
385 }
386 }
387 checkAllCharacteristicsDiscovered();
388 }
389
390private:
391 bool checkAllCharacteristicsDiscovered();
392 void emitErrorAndQuitThread(HRESULT hr);
393 void emitErrorAndQuitThread(const QString &error);
394
395public:
396 QBluetoothUuid mService;
397 ComPtr<IGattDeviceService3> mDeviceService;
398 QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData> mCharacteristicList;
399 uint mCharacteristicsCountToBeDiscovered;
400 quint16 mStartHandle = 0;
401 quint16 mEndHandle = 0;
402 QVector<QBluetoothUuid> mIndicateChars;
403
404signals:
405 void charListObtained(const QBluetoothUuid &service, QHash<QLowEnergyHandle,
406 QLowEnergyServicePrivate::CharData> charList,
407 QVector<QBluetoothUuid> indicateChars,
408 QLowEnergyHandle startHandle, QLowEnergyHandle endHandle);
409 void errorOccured(const QString &error);
410};
411
412bool QWinRTLowEnergyServiceHandlerNew::checkAllCharacteristicsDiscovered()
413{
414 if (mCharacteristicsCountToBeDiscovered == 0) {
415 emit charListObtained(mService, mCharacteristicList, mIndicateChars,
416 mStartHandle, mEndHandle);
417 QThread::currentThread()->quit();
418 return true;
419 }
420
421 return false;
422}
423
424void QWinRTLowEnergyServiceHandlerNew::emitErrorAndQuitThread(HRESULT hr)
425{
426 emitErrorAndQuitThread(qt_error_string(hr));
427}
428
429void QWinRTLowEnergyServiceHandlerNew::emitErrorAndQuitThread(const QString &error)
430{
431 emit errorOccured(error);
432 QThread::currentThread()->quit();
433}
434
435QLowEnergyControllerPrivateWinRTNew::QLowEnergyControllerPrivateWinRTNew()
436 : QLowEnergyControllerPrivate()
437{
438 registerQLowEnergyControllerMetaType();
439 connect(this, &QLowEnergyControllerPrivateWinRTNew::characteristicChanged,
440 this, &QLowEnergyControllerPrivateWinRTNew::handleCharacteristicChanged,
441 Qt::QueuedConnection);
442}
443
444QLowEnergyControllerPrivateWinRTNew::~QLowEnergyControllerPrivateWinRTNew()
445{
446 unregisterFromStatusChanges();
447 unregisterFromValueChanges();
448 mAbortPending = true;
449}
450
451void QLowEnergyControllerPrivateWinRTNew::init()
452{
453}
454
455void QLowEnergyControllerPrivateWinRTNew::connectToDevice()
456{
457 qCDebug(QT_BT_WINRT) << __FUNCTION__;
458 mAbortPending = false;
459 Q_Q(QLowEnergyController);
460 if (remoteDevice.isNull()) {
461 qWarning() << "Invalid/null remote device address";
462 setError(QLowEnergyController::UnknownRemoteDeviceError);
463 return;
464 }
465
466 setState(QLowEnergyController::ConnectingState);
467
468 ComPtr<IBluetoothLEDeviceStatics> deviceStatics;
469 HRESULT hr = GetActivationFactory(
470 HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice).Get(),
471 &deviceStatics);
472 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain device factory", return)
473 ComPtr<IAsyncOperation<BluetoothLEDevice *>> deviceFromIdOperation;
474 hr = deviceStatics->FromBluetoothAddressAsync(remoteDevice.toUInt64(), &deviceFromIdOperation);
475 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not find LE device from address", return)
476 hr = QWinRTFunctions::await(deviceFromIdOperation, mDevice.GetAddressOf(),
477 QWinRTFunctions::ProcessMainThreadEvents, 5000);
478 if (FAILED(hr) || !mDevice) {
479 qCWarning(QT_BT_WINRT) << "Could not find LE device";
480 setError(QLowEnergyController::InvalidBluetoothAdapterError);
481 setState(QLowEnergyController::UnconnectedState);
482 return;
483 }
484 BluetoothConnectionStatus status;
485 hr = mDevice->get_ConnectionStatus(&status);
486 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain device's connection status", return)
487 if (status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) {
488 setState(QLowEnergyController::ConnectedState);
489 emit q->connected();
490 return;
491 }
492
493 QBluetoothLocalDevice localDevice;
494 QBluetoothLocalDevice::Pairing pairing = localDevice.pairingStatus(remoteDevice);
495 if (pairing == QBluetoothLocalDevice::Unpaired)
496 connectToUnpairedDevice();
497 else
498 connectToPairedDevice();
499}
500
501void QLowEnergyControllerPrivateWinRTNew::disconnectFromDevice()
502{
503 qCDebug(QT_BT_WINRT) << __FUNCTION__;
504 Q_Q(QLowEnergyController);
505 setState(QLowEnergyController::ClosingState);
506 unregisterFromValueChanges();
507 unregisterFromStatusChanges();
508 mAbortPending = true;
509 mDevice = nullptr;
510 setState(QLowEnergyController::UnconnectedState);
511 emit q->disconnected();
512}
513
514ComPtr<IGattDeviceService> QLowEnergyControllerPrivateWinRTNew::getNativeService(
515 const QBluetoothUuid &serviceUuid)
516{
517 ComPtr<IGattDeviceService> deviceService;
518 HRESULT hr;
519 hr = mDevice->GetGattService(serviceUuid, &deviceService);
520 if (FAILED(hr))
521 qCDebug(QT_BT_WINRT) << "Could not obtain native service for Uuid" << serviceUuid;
522 return deviceService;
523}
524
525ComPtr<IGattCharacteristic> QLowEnergyControllerPrivateWinRTNew::getNativeCharacteristic(
526 const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid)
527{
528 ComPtr<IGattDeviceService> service = getNativeService(serviceUuid);
529 if (!service)
530 return nullptr;
531
532 ComPtr<IGattDeviceService3> service3;
533 HRESULT hr = service.As(&service3);
534 RETURN_IF_FAILED("Could not cast service", return nullptr);
535
536 ComPtr<IAsyncOperation<GattCharacteristicsResult *>> op;
537 ComPtr<IGattCharacteristicsResult> result;
538 hr = service3->GetCharacteristicsForUuidAsync(charUuid, &op);
539 RETURN_IF_FAILED("Could not obtain native characteristics for service", return nullptr);
540 hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000);
541 RETURN_IF_FAILED("Could not await completion of characteristic operation", return nullptr);
542 GattCommunicationStatus status;
543 hr = result->get_Status(&status);
544 if (FAILED(hr) || status != GattCommunicationStatus_Success) {
545 qErrnoWarning(hr, "Native characteristic operation failed.");
546 return nullptr;
547 }
548 ComPtr<IVectorView<GattCharacteristic *>> characteristics;
549 hr = result->get_Characteristics(&characteristics);
550 RETURN_IF_FAILED("Could not obtain characteristic list.", return nullptr);
551 uint size;
552 hr = characteristics->get_Size(&size);
553 RETURN_IF_FAILED("Could not obtain characteristic list's size.", return nullptr);
554 if (size != 1)
555 qErrnoWarning("More than 1 characteristic found.");
556 ComPtr<IGattCharacteristic> characteristic;
557 hr = characteristics->GetAt(0, &characteristic);
558 RETURN_IF_FAILED("Could not obtain first characteristic for service", return nullptr);
559 return characteristic;
560}
561
562void QLowEnergyControllerPrivateWinRTNew::registerForValueChanges(const QBluetoothUuid &serviceUuid,
563 const QBluetoothUuid &charUuid)
564{
565 qCDebug(QT_BT_WINRT) << "Registering characteristic" << charUuid << "in service"
566 << serviceUuid << "for value changes";
567 for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens)) {
568 GUID guuid;
569 HRESULT hr;
570 hr = entry.characteristic->get_Uuid(&guuid);
571 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic's Uuid")
572 if (QBluetoothUuid(guuid) == charUuid)
573 return;
574 }
575 ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(serviceUuid, charUuid);
576 if (!characteristic) {
577 qCDebug(QT_BT_WINRT).nospace() << "Could not obtain native characteristic " << charUuid
578 << " from service " << serviceUuid << ". Qt will not be able to signal"
579 << " changes for this characteristic.";
580 return;
581 }
582
583 EventRegistrationToken token;
584 HRESULT hr;
585 hr = characteristic->add_ValueChanged(
586 Callback<ValueChangedHandler>(this, &QLowEnergyControllerPrivateWinRTNew::onValueChange).Get(),
587 &token);
588 RETURN_IF_FAILED("Could not register characteristic for value changes", return)
589 mValueChangedTokens.append(ValueChangedEntry(characteristic, token));
590 qCDebug(QT_BT_WINRT) << "Characteristic" << charUuid << "in service"
591 << serviceUuid << "registered for value changes";
592}
593
594void QLowEnergyControllerPrivateWinRTNew::unregisterFromValueChanges()
595{
596 qCDebug(QT_BT_WINRT) << "Unregistering " << mValueChangedTokens.count() << " value change tokens";
597 HRESULT hr;
598 for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens)) {
599 if (!entry.characteristic) {
600 qCWarning(QT_BT_WINRT) << "Unregistering from value changes for characteristic failed."
601 << "Characteristic has been deleted";
602 continue;
603 }
604 hr = entry.characteristic->remove_ValueChanged(entry.token);
605 if (FAILED(hr))
606 qCWarning(QT_BT_WINRT) << "Unregistering from value changes for characteristic failed.";
607 }
608 mValueChangedTokens.clear();
609}
610
611HRESULT QLowEnergyControllerPrivateWinRTNew::onValueChange(IGattCharacteristic *characteristic, IGattValueChangedEventArgs *args)
612{
613 HRESULT hr;
614 quint16 handle;
615 hr = characteristic->get_AttributeHandle(&handle);
616 RETURN_IF_FAILED("Could not obtain characteristic's handle", return S_OK)
617 ComPtr<IBuffer> buffer;
618 hr = args->get_CharacteristicValue(&buffer);
619 RETURN_IF_FAILED("Could not obtain characteristic's value", return S_OK)
620 emit characteristicChanged(handle, byteArrayFromBuffer(buffer));
621 return S_OK;
622}
623
624bool QLowEnergyControllerPrivateWinRTNew::registerForStatusChanges()
625{
626 if (!mDevice)
627 return false;
628
629 qCDebug(QT_BT_WINRT) << __FUNCTION__;
630
631 HRESULT hr;
632 hr = QEventDispatcherWinRT::runOnXamlThread([this]() {
633 HRESULT hr;
634 hr = mDevice->add_ConnectionStatusChanged(
635 Callback<StatusHandler>(this, &QLowEnergyControllerPrivateWinRTNew::onStatusChange).Get(),
636 &mStatusChangedToken);
637 RETURN_IF_FAILED("Could not register connection status callback", return hr)
638 return S_OK;
639 });
640 RETURN_FALSE_IF_FAILED("Could not add status callback on Xaml thread")
641 return true;
642}
643
644void QLowEnergyControllerPrivateWinRTNew::unregisterFromStatusChanges()
645{
646 qCDebug(QT_BT_WINRT) << __FUNCTION__;
647 if (mDevice && mStatusChangedToken.value) {
648 mDevice->remove_ConnectionStatusChanged(mStatusChangedToken);
649 mStatusChangedToken.value = 0;
650 }
651}
652
653HRESULT QLowEnergyControllerPrivateWinRTNew::onStatusChange(IBluetoothLEDevice *dev, IInspectable *)
654{
655 Q_Q(QLowEnergyController);
656 BluetoothConnectionStatus status;
657 HRESULT hr;
658 hr = dev->get_ConnectionStatus(&status);
659 RETURN_IF_FAILED("Could not obtain connection status", return S_OK)
660 if (state == QLowEnergyController::ConnectingState
661 && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) {
662 setState(QLowEnergyController::ConnectedState);
663 emit q->connected();
664 } else if (state != QLowEnergyController::UnconnectedState
665 && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) {
666 invalidateServices();
667 unregisterFromValueChanges();
668 unregisterFromStatusChanges();
669 mDevice = nullptr;
670 setError(QLowEnergyController::RemoteHostClosedError);
671 setState(QLowEnergyController::UnconnectedState);
672 emit q->disconnected();
673 }
674 return S_OK;
675}
676
677void QLowEnergyControllerPrivateWinRTNew::obtainIncludedServices(
678 QSharedPointer<QLowEnergyServicePrivate> servicePointer,
679 ComPtr<IGattDeviceService> service)
680{
681 Q_Q(QLowEnergyController);
682 ComPtr<IGattDeviceService3> service3;
683 HRESULT hr = service.As(&service3);
684 RETURN_IF_FAILED("Could not cast service", return);
685 ComPtr<IAsyncOperation<GattDeviceServicesResult *>> op;
686 hr = service3->GetIncludedServicesAsync(&op);
687 // Some devices return ERROR_ACCESS_DISABLED_BY_POLICY
688 RETURN_IF_FAILED("Could not obtain included services", return);
689 ComPtr<IGattDeviceServicesResult> result;
690 hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000);
691 RETURN_IF_FAILED("Could not await service operation", return);
692 GattCommunicationStatus status;
693 hr = result->get_Status(&status);
694 if (FAILED(hr) || status != GattCommunicationStatus_Success) {
695 qErrnoWarning("Could not obtain list of included services");
696 return;
697 }
698 ComPtr<IVectorView<GattDeviceService *>> includedServices;
699 hr = result->get_Services(&includedServices);
700 RETURN_IF_FAILED("Could not obtain service list", return);
701
702 uint count;
703 hr = includedServices->get_Size(&count);
704 RETURN_IF_FAILED("Could not obtain service list's size", return);
705 for (uint i = 0; i < count; ++i) {
706 ComPtr<IGattDeviceService> includedService;
707 hr = includedServices->GetAt(i, &includedService);
708 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service from list");
709 GUID guuid;
710 hr = includedService->get_Uuid(&guuid);
711 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain included service's Uuid");
712 const QBluetoothUuid includedUuid(guuid);
713 QSharedPointer<QLowEnergyServicePrivate> includedPointer;
714 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__
715 << "Changing service pointer from thread"
716 << QThread::currentThread();
717 if (serviceList.contains(includedUuid)) {
718 includedPointer = serviceList.value(includedUuid);
719 } else {
720 QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate();
721 priv->uuid = includedUuid;
722 priv->setController(this);
723
724 includedPointer = QSharedPointer<QLowEnergyServicePrivate>(priv);
725 serviceList.insert(includedUuid, includedPointer);
726 }
727 includedPointer->type |= QLowEnergyService::IncludedService;
728 servicePointer->includedServices.append(includedUuid);
729
730 obtainIncludedServices(includedPointer, includedService);
731
732 emit q->serviceDiscovered(includedUuid);
733 }
734}
735
736HRESULT QLowEnergyControllerPrivateWinRTNew::onServiceDiscoveryFinished(ABI::Windows::Foundation::IAsyncOperation<GattDeviceServicesResult *> *op, AsyncStatus status)
737{
738 Q_Q(QLowEnergyController);
739 if (status != AsyncStatus::Completed) {
740 qCDebug(QT_BT_WINRT) << "Could not obtain services";
741 return S_OK;
742 }
743 ComPtr<IGattDeviceServicesResult> result;
744 ComPtr<IVectorView<GattDeviceService *>> deviceServices;
745 HRESULT hr = op->GetResults(&result);
746 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service discovery result",
747 return S_OK);
748 GattCommunicationStatus commStatus;
749 hr = result->get_Status(&commStatus);
750 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service discovery status",
751 return S_OK);
752 if (commStatus != GattCommunicationStatus_Success)
753 return S_OK;
754
755 hr = result->get_Services(&deviceServices);
756 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service list",
757 return S_OK);
758
759 uint serviceCount;
760 hr = deviceServices->get_Size(&serviceCount);
761 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service list size",
762 return S_OK);
763 for (uint i = 0; i < serviceCount; ++i) {
764 ComPtr<IGattDeviceService> deviceService;
765 hr = deviceServices->GetAt(i, &deviceService);
766 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service");
767 GUID guuid;
768 hr = deviceService->get_Uuid(&guuid);
769 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service's Uuid");
770 const QBluetoothUuid service(guuid);
771
772 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__
773 << "Changing service pointer from thread"
774 << QThread::currentThread();
775 QSharedPointer<QLowEnergyServicePrivate> pointer;
776 if (serviceList.contains(service)) {
777 pointer = serviceList.value(service);
778 } else {
779 QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate();
780 priv->uuid = service;
781 priv->setController(this);
782
783 pointer = QSharedPointer<QLowEnergyServicePrivate>(priv);
784 serviceList.insert(service, pointer);
785 }
786 pointer->type |= QLowEnergyService::PrimaryService;
787
788 obtainIncludedServices(pointer, deviceService);
789
790 emit q->serviceDiscovered(service);
791 }
792
793 setState(QLowEnergyController::DiscoveredState);
794 emit q->discoveryFinished();
795
796 return S_OK;
797}
798
799void QLowEnergyControllerPrivateWinRTNew::discoverServices()
800{
801 qCDebug(QT_BT_WINRT) << "Service discovery initiated";
802
803 ComPtr<IBluetoothLEDevice3> device3;
804 HRESULT hr = mDevice.As(&device3);
805 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast device", return);
806 ComPtr<IAsyncOperation<GenericAttributeProfile::GattDeviceServicesResult *>> asyncResult;
807 hr = device3->GetGattServicesAsync(&asyncResult);
808 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain services", return);
809 hr = QEventDispatcherWinRT::runOnXamlThread( [asyncResult, this] () {
810 HRESULT hr = asyncResult->put_Completed(
811 Callback<IAsyncOperationCompletedHandler<GenericAttributeProfile::GattDeviceServicesResult *>>(
812 this, &QLowEnergyControllerPrivateWinRTNew::onServiceDiscoveryFinished).Get());
813 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not register service discovery callback",
814 return S_OK)
815 return hr;
816 });
817 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not run registration in Xaml thread",
818 return)
819}
820
821void QLowEnergyControllerPrivateWinRTNew::discoverServiceDetails(const QBluetoothUuid &service)
822{
823 qCDebug(QT_BT_WINRT) << __FUNCTION__ << service;
824 if (!serviceList.contains(service)) {
825 qCWarning(QT_BT_WINRT) << "Discovery done of unknown service:"
826 << service.toString();
827 return;
828 }
829
830 ComPtr<IGattDeviceService> deviceService = getNativeService(service);
831 if (!deviceService) {
832 qCDebug(QT_BT_WINRT) << "Could not obtain native service for uuid " << service;
833 return;
834 }
835
836 //update service data
837 QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
838 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
839 << QThread::currentThread();
840 pointer->setState(QLowEnergyService::DiscoveringServices);
841 ComPtr<IGattDeviceService3> deviceService3;
842 HRESULT hr = deviceService.As(&deviceService3);
843 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast service",
844 pointer, QLowEnergyService::UnknownError, return)
845 ComPtr<IAsyncOperation<GattDeviceServicesResult *>> op;
846 hr = deviceService3->GetIncludedServicesAsync(&op);
847 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain included service list",
848 pointer, QLowEnergyService::UnknownError, return)
849 ComPtr<IGattDeviceServicesResult> result;
850 hr = QWinRTFunctions::await(op, result.GetAddressOf());
851 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await service operation",
852 pointer, QLowEnergyService::UnknownError, return)
853 GattCommunicationStatus status;
854 hr = result->get_Status(&status);
855 if (FAILED(hr) || status != GattCommunicationStatus_Success) {
856 qCDebug(QT_BT_WINRT) << "Obtaining list of included services failed";
857 pointer->setError(QLowEnergyService::UnknownError);
858 return;
859 }
860 ComPtr<IVectorView<GattDeviceService *>> deviceServices;
861 hr = result->get_Services(&deviceServices);
862 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain service list from result",
863 pointer, QLowEnergyService::UnknownError, return)
864 uint serviceCount;
865 hr = deviceServices->get_Size(&serviceCount);
866 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain included service list's size",
867 pointer, QLowEnergyService::UnknownError, return)
868 for (uint i = 0; i < serviceCount; ++i) {
869 ComPtr<IGattDeviceService> includedService;
870 hr = deviceServices->GetAt(i, &includedService);
871 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service from list")
872 GUID guuid;
873 hr = includedService->get_Uuid(&guuid);
874 WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service Uuid")
875
876 const QBluetoothUuid service(guuid);
877 if (service.isNull()) {
878 qCDebug(QT_BT_WINRT) << "Could not find service";
879 continue;
880 }
881
882 pointer->includedServices.append(service);
883
884 // update the type of the included service
885 QSharedPointer<QLowEnergyServicePrivate> otherService = serviceList.value(service);
886 if (!otherService.isNull())
887 otherService->type |= QLowEnergyService::IncludedService;
888 }
889
890 QWinRTLowEnergyServiceHandlerNew *worker
891 = new QWinRTLowEnergyServiceHandlerNew(service, deviceService3);
892 QThread *thread = new QThread;
893 worker->moveToThread(thread);
894 connect(thread, &QThread::started, worker, &QWinRTLowEnergyServiceHandlerNew::obtainCharList);
895 connect(thread, &QThread::finished, thread, &QObject::deleteLater);
896 connect(thread, &QThread::finished, worker, &QObject::deleteLater);
897 connect(worker, &QWinRTLowEnergyServiceHandlerNew::errorOccured,
898 this, &QLowEnergyControllerPrivateWinRTNew::handleServiceHandlerError);
899 connect(worker, &QWinRTLowEnergyServiceHandlerNew::charListObtained,
900 [this, thread](const QBluetoothUuid &service, QHash<QLowEnergyHandle,
901 QLowEnergyServicePrivate::CharData> charList, QVector<QBluetoothUuid> indicateChars,
902 QLowEnergyHandle startHandle, QLowEnergyHandle endHandle) {
903 if (!serviceList.contains(service)) {
904 qCWarning(QT_BT_WINRT) << "Discovery done of unknown service:"
905 << service.toString();
906 return;
907 }
908
909 QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
910 pointer->startHandle = startHandle;
911 pointer->endHandle = endHandle;
912 pointer->characteristicList = charList;
913
914 HRESULT hr;
915 hr = QEventDispatcherWinRT::runOnXamlThread([indicateChars, service, this]() {
916 for (const QBluetoothUuid &indicateChar : qAsConst(indicateChars))
917 registerForValueChanges(service, indicateChar);
918 return S_OK;
919 });
920 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register for value changes in Xaml thread",
921 pointer, QLowEnergyService::UnknownError, return)
922
923 pointer->setState(QLowEnergyService::ServiceDiscovered);
924 thread->exit(0);
925 });
926 thread->start();
927}
928
929void QLowEnergyControllerPrivateWinRTNew::startAdvertising(
930 const QLowEnergyAdvertisingParameters &,
931 const QLowEnergyAdvertisingData &,
932 const QLowEnergyAdvertisingData &)
933{
934 setError(QLowEnergyController::AdvertisingError);
935 Q_UNIMPLEMENTED();
936}
937
938void QLowEnergyControllerPrivateWinRTNew::stopAdvertising()
939{
940 Q_UNIMPLEMENTED();
941}
942
943void QLowEnergyControllerPrivateWinRTNew::requestConnectionUpdate(const QLowEnergyConnectionParameters &)
944{
945 Q_UNIMPLEMENTED();
946}
947
948void QLowEnergyControllerPrivateWinRTNew::readCharacteristic(
949 const QSharedPointer<QLowEnergyServicePrivate> service,
950 const QLowEnergyHandle charHandle)
951{
952 qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle;
953 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
954 << QThread::currentThread();
955 Q_ASSERT(!service.isNull());
956 if (role == QLowEnergyController::PeripheralRole) {
957 service->setError(QLowEnergyService::CharacteristicReadError);
958 Q_UNIMPLEMENTED();
959 return;
960 }
961
962 if (!service->characteristicList.contains(charHandle)) {
963 qCDebug(QT_BT_WINRT) << charHandle << "could not be found in service" << service->uuid;
964 service->setError(QLowEnergyService::CharacteristicReadError);
965 return;
966 }
967
968 HRESULT hr;
969 hr = QEventDispatcherWinRT::runOnXamlThread([charHandle, service, this]() {
970 const QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
971 if (!(charData.properties & QLowEnergyCharacteristic::Read))
972 qCDebug(QT_BT_WINRT) << "Read flag is not set for characteristic" << charData.uuid;
973
974 ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, charData.uuid);
975 if (!characteristic) {
976 qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid
977 << "from service" << service->uuid;
978 service->setError(QLowEnergyService::CharacteristicReadError);
979 return S_OK;
980 }
981 ComPtr<IAsyncOperation<GattReadResult*>> readOp;
982 HRESULT hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp);
983 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not read characteristic",
984 service, QLowEnergyService::CharacteristicReadError, return S_OK)
985 auto readCompletedLambda = [charData, charHandle, service]
986 (IAsyncOperation<GattReadResult*> *op, AsyncStatus status)
987 {
988 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
989 qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "read operation failed.";
990 service->setError(QLowEnergyService::CharacteristicReadError);
991 return S_OK;
992 }
993 ComPtr<IGattReadResult> characteristicValue;
994 HRESULT hr;
995 hr = op->GetResults(&characteristicValue);
996 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for characteristic",
997 service, QLowEnergyService::CharacteristicReadError, return S_OK)
998
999 const QByteArray value = byteArrayFromGattResult(characteristicValue);
1000 QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
1001 charData.value = value;
1002 service->characteristicList.insert(charHandle, charData);
1003 emit service->characteristicRead(QLowEnergyCharacteristic(service, charHandle), value);
1004 return S_OK;
1005 };
1006 hr = readOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattReadResult *>>(
1007 readCompletedLambda).Get());
1008 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register characteristic read callback",
1009 service, QLowEnergyService::CharacteristicReadError, return S_OK)
1010 return S_OK;
1011 });
1012 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not run registration on Xaml thread",
1013 service, QLowEnergyService::CharacteristicReadError, return)
1014}
1015
1016void QLowEnergyControllerPrivateWinRTNew::readDescriptor(
1017 const QSharedPointer<QLowEnergyServicePrivate> service,
1018 const QLowEnergyHandle charHandle,
1019 const QLowEnergyHandle descHandle)
1020{
1021 qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << descHandle;
1022 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1023 << QThread::currentThread();
1024 Q_ASSERT(!service.isNull());
1025 if (role == QLowEnergyController::PeripheralRole) {
1026 service->setError(QLowEnergyService::DescriptorReadError);
1027 Q_UNIMPLEMENTED();
1028 return;
1029 }
1030
1031 if (!service->characteristicList.contains(charHandle)) {
1032 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "in characteristic" << charHandle
1033 << "cannot be found in service" << service->uuid;
1034 service->setError(QLowEnergyService::DescriptorReadError);
1035 return;
1036 }
1037
1038 HRESULT hr;
1039 hr = QEventDispatcherWinRT::runOnXamlThread([charHandle, descHandle, service, this]() {
1040 const QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
1041 ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, charData.uuid);
1042 if (!characteristic) {
1043 qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid
1044 << "from service" << service->uuid;
1045 service->setError(QLowEnergyService::DescriptorReadError);
1046 return S_OK;
1047 }
1048
1049 // Get native descriptor
1050 if (!charData.descriptorList.contains(descHandle))
1051 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "cannot be found in characteristic" << charHandle;
1052 const QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle);
1053 const QBluetoothUuid descUuid = descData.uuid;
1054 if (descUuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) {
1055 ComPtr<IAsyncOperation<ClientCharConfigDescriptorResult *>> readOp;
1056 HRESULT hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp);
1057 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not read client characteristic configuration",
1058 service, QLowEnergyService::DescriptorReadError, return S_OK)
1059 auto readCompletedLambda = [charHandle, descHandle, service]
1060 (IAsyncOperation<ClientCharConfigDescriptorResult *> *op, AsyncStatus status)
1061 {
1062 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
1063 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "read operation failed";
1064 service->setError(QLowEnergyService::DescriptorReadError);
1065 return S_OK;
1066 }
1067 ComPtr<IClientCharConfigDescriptorResult> iValue;
1068 HRESULT hr;
1069 hr = op->GetResults(&iValue);
1070 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for descriptor",
1071 service, QLowEnergyService::DescriptorReadError, return S_OK)
1072 GattClientCharacteristicConfigurationDescriptorValue value;
1073 hr = iValue->get_ClientCharacteristicConfigurationDescriptor(&value);
1074 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain value for descriptor",
1075 service, QLowEnergyService::DescriptorReadError, return S_OK)
1076 quint16 result = 0;
1077 bool correct = false;
1078 if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) {
1079 result |= QLowEnergyCharacteristic::Indicate;
1080 correct = true;
1081 }
1082 if (value & GattClientCharacteristicConfigurationDescriptorValue_Notify) {
1083 result |= QLowEnergyCharacteristic::Notify;
1084 correct = true;
1085 }
1086 if (value == GattClientCharacteristicConfigurationDescriptorValue_None)
1087 correct = true;
1088 if (!correct) {
1089 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle
1090 << "read operation failed. Obtained unexpected value.";
1091 service->setError(QLowEnergyService::DescriptorReadError);
1092 return S_OK;
1093 }
1094 QLowEnergyServicePrivate::DescData descData;
1095 descData.uuid = QBluetoothUuid::ClientCharacteristicConfiguration;
1096 descData.value = QByteArray(2, Qt::Uninitialized);
1097 qToLittleEndian(result, descData.value.data());
1098 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
1099 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
1100 descData.value);
1101 return S_OK;
1102 };
1103 hr = readOp->put_Completed(
1104 Callback<IAsyncOperationCompletedHandler<ClientCharConfigDescriptorResult *>>(
1105 readCompletedLambda).Get());
1106 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor read callback",
1107 service, QLowEnergyService::DescriptorReadError, return S_OK)
1108 return S_OK;
1109 } else {
1110 ComPtr<IGattCharacteristic3> characteristic3;
1111 HRESULT hr = characteristic.As(&characteristic3);
1112 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast characteristic",
1113 service, QLowEnergyService::DescriptorReadError, return S_OK)
1114 ComPtr<IAsyncOperation<GattDescriptorsResult *>> op;
1115 hr = characteristic3->GetDescriptorsForUuidAsync(descData.uuid, &op);
1116 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor for uuid",
1117 service, QLowEnergyService::DescriptorReadError, return S_OK)
1118 ComPtr<IGattDescriptorsResult> result;
1119 hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000);
1120 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await descritpor read result",
1121 service, QLowEnergyService::DescriptorReadError, return S_OK)
1122
1123 GattCommunicationStatus commStatus;
1124 hr = result->get_Status(&commStatus);
1125 if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
1126 qErrnoWarning("Could not obtain list of descriptors");
1127 service->setError(QLowEnergyService::DescriptorReadError);
1128 return S_OK;
1129 }
1130
1131 ComPtr<IVectorView<GattDescriptor *>> descriptors;
1132 hr = result->get_Descriptors(&descriptors);
1133 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor list",
1134 service, QLowEnergyService::DescriptorReadError, return S_OK)
1135 uint size;
1136 hr = descriptors->get_Size(&size);
1137 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await descritpor list's size",
1138 service, QLowEnergyService::DescriptorReadError, return S_OK)
1139 if (size == 0) {
1140 qCWarning(QT_BT_WINRT) << "No descriptor with uuid" << descData.uuid << "was found.";
1141 service->setError(QLowEnergyService::DescriptorReadError);
1142 return S_OK;
1143 } else if (size > 1) {
1144 qCWarning(QT_BT_WINRT) << "There is more than 1 descriptor with uuid" << descData.uuid;
1145 }
1146
1147 ComPtr<IGattDescriptor> descriptor;
1148 hr = descriptors->GetAt(0, &descriptor);
1149 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descritpor from list",
1150 service, QLowEnergyService::DescriptorReadError, return S_OK)
1151 ComPtr<IAsyncOperation<GattReadResult*>> readOp;
1152 hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp);
1153 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not read descriptor value",
1154 service, QLowEnergyService::DescriptorReadError, return S_OK)
1155 auto readCompletedLambda = [charHandle, descHandle, descUuid, service]
1156 (IAsyncOperation<GattReadResult*> *op, AsyncStatus status)
1157 {
1158 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
1159 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "read operation failed";
1160 service->setError(QLowEnergyService::DescriptorReadError);
1161 return S_OK;
1162 }
1163 ComPtr<IGattReadResult> descriptorValue;
1164 HRESULT hr;
1165 hr = op->GetResults(&descriptorValue);
1166 if (FAILED(hr)) {
1167 qCDebug(QT_BT_WINRT) << "Could not obtain result for descriptor" << descHandle;
1168 service->setError(QLowEnergyService::DescriptorReadError);
1169 return S_OK;
1170 }
1171 QLowEnergyServicePrivate::DescData descData;
1172 descData.uuid = descUuid;
1173 if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription)
1174 descData.value = byteArrayFromGattResult(descriptorValue, true);
1175 else
1176 descData.value = byteArrayFromGattResult(descriptorValue);
1177 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
1178 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
1179 descData.value);
1180 return S_OK;
1181 };
1182 hr = readOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattReadResult *>>(
1183 readCompletedLambda).Get());
1184 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor read callback",
1185 service, QLowEnergyService::DescriptorReadError, return S_OK)
1186 return S_OK;
1187 }
1188 });
1189 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not run registration on Xaml thread",
1190 service, QLowEnergyService::DescriptorReadError, return)
1191}
1192
1193void QLowEnergyControllerPrivateWinRTNew::writeCharacteristic(
1194 const QSharedPointer<QLowEnergyServicePrivate> service,
1195 const QLowEnergyHandle charHandle,
1196 const QByteArray &newValue,
1197 QLowEnergyService::WriteMode mode)
1198{
1199 qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << newValue << mode;
1200 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1201 << QThread::currentThread();
1202 Q_ASSERT(!service.isNull());
1203 if (role == QLowEnergyController::PeripheralRole) {
1204 service->setError(QLowEnergyService::CharacteristicWriteError);
1205 Q_UNIMPLEMENTED();
1206 return;
1207 }
1208 if (!service->characteristicList.contains(charHandle)) {
1209 qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "cannot be found in service"
1210 << service->uuid;
1211 service->setError(QLowEnergyService::CharacteristicWriteError);
1212 return;
1213 }
1214
1215 QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
1216 const bool writeWithResponse = mode == QLowEnergyService::WriteWithResponse;
1217 if (!(charData.properties & (writeWithResponse ? QLowEnergyCharacteristic::Write
1218 : QLowEnergyCharacteristic::WriteNoResponse)))
1219 qCDebug(QT_BT_WINRT) << "Write flag is not set for characteristic" << charHandle;
1220
1221 HRESULT hr;
1222 hr = QEventDispatcherWinRT::runOnXamlThread([charData, charHandle, this, service, newValue,
1223 writeWithResponse]() {
1224 ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid,
1225 charData.uuid);
1226 if (!characteristic) {
1227 qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid
1228 << "from service" << service->uuid;
1229 service->setError(QLowEnergyService::CharacteristicWriteError);
1230 return S_OK;
1231 }
1232 ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory;
1233 HRESULT hr = GetActivationFactory(
1234 HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
1235 &bufferFactory);
1236 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain buffer factory",
1237 service, QLowEnergyService::CharacteristicWriteError, return S_OK)
1238 ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
1239 const quint32 length = quint32(newValue.length());
1240 hr = bufferFactory->Create(length, &buffer);
1241 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not create buffer",
1242 service, QLowEnergyService::CharacteristicWriteError, return S_OK)
1243 hr = buffer->put_Length(length);
1244 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer length",
1245 service, QLowEnergyService::CharacteristicWriteError, return S_OK)
1246 ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess;
1247 hr = buffer.As(&byteAccess);
1248 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast buffer",
1249 service, QLowEnergyService::CharacteristicWriteError, return S_OK)
1250 byte *bytes;
1251 hr = byteAccess->Buffer(&bytes);
1252 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer",
1253 service, QLowEnergyService::CharacteristicWriteError, return S_OK)
1254 memcpy(bytes, newValue, length);
1255 ComPtr<IAsyncOperation<GattCommunicationStatus>> writeOp;
1256 GattWriteOption option = writeWithResponse ? GattWriteOption_WriteWithResponse
1257 : GattWriteOption_WriteWithoutResponse;
1258 hr = characteristic->WriteValueWithOptionAsync(buffer.Get(), option, &writeOp);
1259 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could write characteristic",
1260 service, QLowEnergyService::CharacteristicWriteError, return S_OK)
1261 QPointer<QLowEnergyControllerPrivateWinRTNew> thisPtr(this);
1262 auto writeCompletedLambda = [charData, charHandle, newValue, service, writeWithResponse, thisPtr]
1263 (IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status)
1264 {
1265 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
1266 qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "write operation failed";
1267 service->setError(QLowEnergyService::CharacteristicWriteError);
1268 return S_OK;
1269 }
1270 GattCommunicationStatus result;
1271 HRESULT hr;
1272 hr = op->GetResults(&result);
1273 if (hr == E_BLUETOOTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH) {
1274 qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle
1275 << "write operation was tried with invalid value length";
1276 service->setError(QLowEnergyService::CharacteristicWriteError);
1277 return S_OK;
1278 }
1279 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain characteristic write result",
1280 service, QLowEnergyService::CharacteristicWriteError, return S_OK)
1281 if (result != GattCommunicationStatus_Success) {
1282 qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "write operation failed";
1283 service->setError(QLowEnergyService::CharacteristicWriteError);
1284 return S_OK;
1285 }
1286 // only update cache when property is readable. Otherwise it remains
1287 // empty.
1288 if (charData.properties & QLowEnergyCharacteristic::Read)
1289 thisPtr->updateValueOfCharacteristic(charHandle, newValue, false);
1290 if (writeWithResponse)
1291 emit service->characteristicWritten(QLowEnergyCharacteristic(service, charHandle),
1292 newValue);
1293 return S_OK;
1294 };
1295 hr = writeOp->put_Completed(
1296 Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus>>(
1297 writeCompletedLambda).Get());
1298 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register characteristic write callback",
1299 service, QLowEnergyService::CharacteristicWriteError, return S_OK)
1300 return S_OK;
1301 });
1302 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not run registration on Xaml thread",
1303 service, QLowEnergyService::CharacteristicWriteError, return)
1304}
1305
1306void QLowEnergyControllerPrivateWinRTNew::writeDescriptor(
1307 const QSharedPointer<QLowEnergyServicePrivate> service,
1308 const QLowEnergyHandle charHandle,
1309 const QLowEnergyHandle descHandle,
1310 const QByteArray &newValue)
1311{
1312 qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << descHandle << newValue;
1313 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1314 << QThread::currentThread();
1315 Q_ASSERT(!service.isNull());
1316 if (role == QLowEnergyController::PeripheralRole) {
1317 service->setError(QLowEnergyService::DescriptorWriteError);
1318 Q_UNIMPLEMENTED();
1319 return;
1320 }
1321
1322 if (!service->characteristicList.contains(charHandle)) {
1323 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "in characteristic" << charHandle
1324 << "could not be found in service" << service->uuid;
1325 service->setError(QLowEnergyService::DescriptorWriteError);
1326 return;
1327 }
1328
1329 HRESULT hr;
1330 hr = QEventDispatcherWinRT::runOnXamlThread([charHandle, descHandle, this, service, newValue]() {
1331 const QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
1332 ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, charData.uuid);
1333 if (!characteristic) {
1334 qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid
1335 << "from service" << service->uuid;
1336 service->setError(QLowEnergyService::DescriptorWriteError);
1337 return S_OK;
1338 }
1339
1340 // Get native descriptor
1341 if (!charData.descriptorList.contains(descHandle))
1342 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "could not be found in Characteristic"
1343 << charHandle;
1344
1345 QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle);
1346 if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) {
1347 GattClientCharacteristicConfigurationDescriptorValue value;
1348 quint16 intValue = qFromLittleEndian<quint16>(newValue);
1349 if (intValue & GattClientCharacteristicConfigurationDescriptorValue_Indicate
1350 && intValue & GattClientCharacteristicConfigurationDescriptorValue_Notify) {
1351 qCWarning(QT_BT_WINRT) << "Setting both Indicate and Notify is not supported on WinRT";
1352 value = GattClientCharacteristicConfigurationDescriptorValue(
1353 (GattClientCharacteristicConfigurationDescriptorValue_Indicate
1354 | GattClientCharacteristicConfigurationDescriptorValue_Notify));
1355 } else if (intValue & GattClientCharacteristicConfigurationDescriptorValue_Indicate) {
1356 value = GattClientCharacteristicConfigurationDescriptorValue_Indicate;
1357 } else if (intValue & GattClientCharacteristicConfigurationDescriptorValue_Notify) {
1358 value = GattClientCharacteristicConfigurationDescriptorValue_Notify;
1359 } else if (intValue == 0) {
1360 value = GattClientCharacteristicConfigurationDescriptorValue_None;
1361 } else {
1362 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle
1363 << "write operation failed: Invalid value";
1364 service->setError(QLowEnergyService::DescriptorWriteError);
1365 return S_OK;
1366 }
1367 ComPtr<IAsyncOperation<enum GattCommunicationStatus>> writeOp;
1368 HRESULT hr = characteristic->WriteClientCharacteristicConfigurationDescriptorAsync(value, &writeOp);
1369 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not write client characteristic configuration",
1370 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1371 QPointer<QLowEnergyControllerPrivateWinRTNew> thisPtr(this);
1372 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
1373 (IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status)
1374 {
1375 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
1376 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed";
1377 service->setError(QLowEnergyService::DescriptorWriteError);
1378 return S_OK;
1379 }
1380 GattCommunicationStatus result;
1381 HRESULT hr;
1382 hr = op->GetResults(&result);
1383 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for descriptor",
1384 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1385 if (result != GattCommunicationStatus_Success) {
1386 qCWarning(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed";
1387 service->setError(QLowEnergyService::DescriptorWriteError);
1388 return S_OK;
1389 }
1390 thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue, false);
1391 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
1392 newValue);
1393 return S_OK;
1394 };
1395 hr = writeOp->put_Completed(
1396 Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus >>(
1397 writeCompletedLambda).Get());
1398 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor write callback",
1399 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1400 } else {
1401 ComPtr<IGattCharacteristic3> characteristic3;
1402 HRESULT hr = characteristic.As(&characteristic3);
1403 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast characteristic",
1404 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1405 ComPtr<IAsyncOperation<GattDescriptorsResult *>> op;
1406 hr = characteristic3->GetDescriptorsForUuidAsync(descData.uuid, &op);
1407 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor from Uuid",
1408 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1409 ComPtr<IGattDescriptorsResult> result;
1410 hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000);
1411 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await descriptor operation",
1412 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1413 GattCommunicationStatus commStatus;
1414 hr = result->get_Status(&commStatus);
1415 if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
1416 qCWarning(QT_BT_WINRT) << "Descriptor operation failed";
1417 service->setError(QLowEnergyService::DescriptorWriteError);
1418 return S_OK;
1419 }
1420 ComPtr<IVectorView<GattDescriptor *>> descriptors;
1421 hr = result->get_Descriptors(&descriptors);
1422 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain list of descriptors",
1423 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1424 uint size;
1425 hr = descriptors->get_Size(&size);
1426 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain list of descriptors' size",
1427 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1428 if (size == 0) {
1429 qCWarning(QT_BT_WINRT) << "No descriptor with uuid" << descData.uuid << "was found.";
1430 return S_OK;
1431 } else if (size > 1) {
1432 qCWarning(QT_BT_WINRT) << "There is more than 1 descriptor with uuid" << descData.uuid;
1433 }
1434 ComPtr<IGattDescriptor> descriptor;
1435 hr = descriptors->GetAt(0, &descriptor);
1436 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor",
1437 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1438 ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory;
1439 hr = GetActivationFactory(
1440 HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
1441 &bufferFactory);
1442 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain buffer factory",
1443 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1444 ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
1445 const quint32 length = quint32(newValue.length());
1446 hr = bufferFactory->Create(length, &buffer);
1447 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not create buffer",
1448 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1449 hr = buffer->put_Length(length);
1450 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer length",
1451 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1452 ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess;
1453 hr = buffer.As(&byteAccess);
1454 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast buffer",
1455 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1456 byte *bytes;
1457 hr = byteAccess->Buffer(&bytes);
1458 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer",
1459 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1460 memcpy(bytes, newValue, length);
1461 ComPtr<IAsyncOperation<GattCommunicationStatus>> writeOp;
1462 hr = descriptor->WriteValueAsync(buffer.Get(), &writeOp);
1463 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not write descriptor value",
1464 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1465 QPointer<QLowEnergyControllerPrivateWinRTNew> thisPtr(this);
1466 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, thisPtr]
1467 (IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status)
1468 {
1469 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
1470 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed";
1471 service->setError(QLowEnergyService::DescriptorWriteError);
1472 return S_OK;
1473 }
1474 GattCommunicationStatus result;
1475 HRESULT hr;
1476 hr = op->GetResults(&result);
1477 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for descriptor",
1478 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1479 if (result != GattCommunicationStatus_Success) {
1480 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed";
1481 service->setError(QLowEnergyService::DescriptorWriteError);
1482 return S_OK;
1483 }
1484 thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue, false);
1485 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
1486 newValue);
1487 return S_OK;
1488 };
1489 hr = writeOp->put_Completed(
1490 Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus>>(
1491 writeCompletedLambda).Get());
1492 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor write callback",
1493 service, QLowEnergyService::DescriptorWriteError, return S_OK)
1494 return S_OK;
1495 }
1496 return S_OK;
1497 });
1498 CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not run registration on Xaml thread",
1499 service, QLowEnergyService::DescriptorWriteError, return)
1500}
1501
1502
1503void QLowEnergyControllerPrivateWinRTNew::addToGenericAttributeList(const QLowEnergyServiceData &,
1504 QLowEnergyHandle)
1505{
1506 Q_UNIMPLEMENTED();
1507}
1508
1509void QLowEnergyControllerPrivateWinRTNew::handleCharacteristicChanged(
1510 quint16 charHandle, const QByteArray &data)
1511{
1512 qCDebug(QT_BT_WINRT) << __FUNCTION__ << charHandle << data;
1513 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1514 << QThread::currentThread();
1515 QSharedPointer<QLowEnergyServicePrivate> service =
1516 serviceForHandle(charHandle);
1517 if (service.isNull())
1518 return;
1519
1520 qCDebug(QT_BT_WINRT) << "Characteristic change notification" << service->uuid
1521 << charHandle << data.toHex();
1522
1523 QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle);
1524 if (!characteristic.isValid()) {
1525 qCWarning(QT_BT_WINRT) << "characteristicChanged: Cannot find characteristic";
1526 return;
1527 }
1528
1529 // only update cache when property is readable. Otherwise it remains
1530 // empty.
1531 if (characteristic.properties() & QLowEnergyCharacteristic::Read)
1532 updateValueOfCharacteristic(characteristic.attributeHandle(),
1533 data, false);
1534 emit service->characteristicChanged(characteristic, data);
1535}
1536
1537void QLowEnergyControllerPrivateWinRTNew::handleServiceHandlerError(const QString &error)
1538{
1539 if (state != QLowEnergyController::DiscoveringState)
1540 return;
1541
1542 qCWarning(QT_BT_WINRT) << "Error while discovering services:" << error;
1543 setState(QLowEnergyController::UnconnectedState);
1544 setError(QLowEnergyController::ConnectionError);
1545}
1546
1547void QLowEnergyControllerPrivateWinRTNew::connectToPairedDevice()
1548{
1549 Q_Q(QLowEnergyController);
1550 ComPtr<IBluetoothLEDevice3> device3;
1551 HRESULT hr = mDevice.As(&device3);
1552 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast device", return)
1553 ComPtr<IAsyncOperation<GattDeviceServicesResult *>> deviceServicesOp;
1554 while (!mAbortPending) {
1555 hr = device3->GetGattServicesAsync(&deviceServicesOp);
1556 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain services", return)
1557 ComPtr<IGattDeviceServicesResult> deviceServicesResult;
1558 hr = QWinRTFunctions::await(deviceServicesOp, deviceServicesResult.GetAddressOf(),
1559 QWinRTFunctions::ProcessThreadEvents, 5000);
1560 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await services operation", return)
1561
1562 GattCommunicationStatus commStatus;
1563 hr = deviceServicesResult->get_Status(&commStatus);
1564 if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
1565 qCWarning(QT_BT_WINRT()) << "Service operation failed";
1566 setError(QLowEnergyController::ConnectionError);
1567 setState(QLowEnergyController::UnconnectedState);
1568 unregisterFromStatusChanges();
1569 return;
1570 }
1571
1572 ComPtr<IVectorView <GattDeviceService *>> deviceServices;
1573 hr = deviceServicesResult->get_Services(&deviceServices);
1574 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain list of services", return)
1575 uint serviceCount;
1576 hr = deviceServices->get_Size(&serviceCount);
1577 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service count", return)
1578
1579 if (serviceCount == 0) {
1580 qCWarning(QT_BT_WINRT()) << "Found devices without services";
1581 setError(QLowEnergyController::ConnectionError);
1582 setState(QLowEnergyController::UnconnectedState);
1583 unregisterFromStatusChanges();
1584 return;
1585 }
1586
1587 // Windows automatically connects to the device as soon as a service value is read/written.
1588 // Thus we read one value in order to establish the connection.
1589 for (uint i = 0; i < serviceCount; ++i) {
1590 ComPtr<IGattDeviceService> service;
1591 hr = deviceServices->GetAt(i, &service);
1592 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service", return);
1593 ComPtr<IGattDeviceService3> service3;
1594 hr = service.As(&service3);
1595 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast service", return);
1596 ComPtr<IAsyncOperation<GattCharacteristicsResult *>> characteristicsOp;
1597 hr = service3->GetCharacteristicsAsync(&characteristicsOp);
1598 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic", return);
1599 ComPtr<IGattCharacteristicsResult> characteristicsResult;
1600 hr = QWinRTFunctions::await(characteristicsOp, characteristicsResult.GetAddressOf(),
1601 QWinRTFunctions::ProcessThreadEvents, 5000);
1602 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await characteristic operation", return);
1603 GattCommunicationStatus commStatus;
1604 hr = characteristicsResult->get_Status(&commStatus);
1605 if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
1606 qCWarning(QT_BT_WINRT) << "Characteristic operation failed";
1607 break;
1608 }
1609 ComPtr<IVectorView<GattCharacteristic *>> characteristics;
1610 hr = characteristicsResult->get_Characteristics(&characteristics);
1611 if (hr == E_ACCESSDENIED) {
1612 // Everything will work as expected up until this point if the manifest capabilties
1613 // for bluetooth LE are not set.
1614 qCWarning(QT_BT_WINRT) << "Could not obtain characteristic list. Please check your "
1615 "manifest capabilities";
1616 setState(QLowEnergyController::UnconnectedState);
1617 setError(QLowEnergyController::ConnectionError);
1618 unregisterFromStatusChanges();
1619 return;
1620 }
1621 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic list", return);
1622 uint characteristicsCount;
1623 hr = characteristics->get_Size(&characteristicsCount);
1624 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic list's size", return);
1625 for (uint j = 0; j < characteristicsCount; ++j) {
1626 ComPtr<IGattCharacteristic> characteristic;
1627 hr = characteristics->GetAt(j, &characteristic);
1628 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic", return);
1629 ComPtr<IAsyncOperation<GattReadResult *>> op;
1630 GattCharacteristicProperties props;
1631 hr = characteristic->get_CharacteristicProperties(&props);
1632 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic's properties", return);
1633 if (!(props & GattCharacteristicProperties_Read))
1634 continue;
1635 hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode::BluetoothCacheMode_Uncached, &op);
1636 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not read characteristic value", return);
1637 ComPtr<IGattReadResult> result;
1638 hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessThreadEvents, 500);
1639 // E_ILLEGAL_METHOD_CALL will be the result for a device, that is not reachable at
1640 // the moment. In this case we should jump back into the outer loop and keep trying.
1641 if (hr == E_ILLEGAL_METHOD_CALL)
1642 break;
1643 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await characteristic read", return);
1644 ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
1645 hr = result->get_Value(&buffer);
1646 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic value", return);
1647 if (!buffer) {
1648 qCDebug(QT_BT_WINRT) << "Problem reading value";
1649 break;
1650 }
1651
1652 setState(QLowEnergyController::ConnectedState);
1653 emit q->connected();
1654 if (!registerForStatusChanges()) {
1655 setError(QLowEnergyController::ConnectionError);
1656 setState(QLowEnergyController::UnconnectedState);
1657 return;
1658 }
1659 return;
1660 }
1661 }
1662 }
1663}
1664
1665void QLowEnergyControllerPrivateWinRTNew::connectToUnpairedDevice()
1666{
1667 if (!registerForStatusChanges()) {
1668 setError(QLowEnergyController::ConnectionError);
1669 setState(QLowEnergyController::UnconnectedState);
1670 return;
1671 }
1672 ComPtr<IBluetoothLEDevice3> device3;
1673 HRESULT hr = mDevice.As(&device3);
1674 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast device", return)
1675 ComPtr<IGattDeviceServicesResult> deviceServicesResult;
1676 while (!mAbortPending) {
1677 ComPtr<IAsyncOperation<GattDeviceServicesResult *>> deviceServicesOp;
1678 hr = device3->GetGattServicesAsync(&deviceServicesOp);
1679 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain services", return)
1680 hr = QWinRTFunctions::await(deviceServicesOp, deviceServicesResult.GetAddressOf(),
1681 QWinRTFunctions::ProcessMainThreadEvents);
1682 CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await services operation", return)
1683
1684 GattCommunicationStatus commStatus;
1685 hr = deviceServicesResult->get_Status(&commStatus);
1686 if (commStatus == GattCommunicationStatus_Unreachable)
1687 continue;
1688
1689 if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
1690 qCWarning(QT_BT_WINRT()) << "Service operation failed";
1691 setError(QLowEnergyController::ConnectionError);
1692 setState(QLowEnergyController::UnconnectedState);
1693 unregisterFromStatusChanges();
1694 return;
1695 }
1696
1697 break;
1698 }
1699}
1700
1701QT_END_NAMESPACE
1702
1703#include "qlowenergycontroller_winrt_new.moc"
1704

Warning: That file was not part of the compilation database. It may have many parsing errors.