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_p.h"
41#include "qbluetoothutils_winrt_p.h"
42
43#include <QtBluetooth/QLowEnergyCharacteristicData>
44#include <QtBluetooth/QLowEnergyDescriptorData>
45
46#ifdef CLASSIC_APP_BUILD
47#define Q_OS_WINRT
48#endif
49#include <QtCore/qfunctions_winrt.h>
50#include <QtCore/QtEndian>
51#include <QtCore/QLoggingCategory>
52#include <private/qeventdispatcher_winrt_p.h>
53
54#include <functional>
55#include <robuffer.h>
56#include <windows.devices.enumeration.h>
57#include <windows.devices.bluetooth.h>
58#include <windows.foundation.collections.h>
59#include <windows.storage.streams.h>
60
61using namespace Microsoft::WRL;
62using namespace Microsoft::WRL::Wrappers;
63using namespace ABI::Windows::Foundation;
64using namespace ABI::Windows::Foundation::Collections;
65using namespace ABI::Windows::Devices;
66using namespace ABI::Windows::Devices::Bluetooth;
67using namespace ABI::Windows::Devices::Bluetooth::GenericAttributeProfile;
68using namespace ABI::Windows::Devices::Enumeration;
69using namespace ABI::Windows::Storage::Streams;
70
71QT_BEGIN_NAMESPACE
72
73typedef ITypedEventHandler<BluetoothLEDevice *, IInspectable *> StatusHandler;
74typedef ITypedEventHandler<GattCharacteristic *, GattValueChangedEventArgs *> ValueChangedHandler;
75typedef GattReadClientCharacteristicConfigurationDescriptorResult ClientCharConfigDescriptorResult;
76typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharConfigDescriptorResult;
77
78Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
79Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT_SERVICE_THREAD)
80
81static QByteArray byteArrayFromGattResult(const ComPtr<IGattReadResult> &gattResult, bool isWCharString = false)
82{
83 ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
84 HRESULT hr;
85 hr = gattResult->get_Value(&buffer);
86 Q_ASSERT_SUCCEEDED(hr);
87 return byteArrayFromBuffer(buffer, isWCharString);
88}
89
90class QWinRTLowEnergyServiceHandler : public QObject
91{
92 Q_OBJECT
93public:
94 QWinRTLowEnergyServiceHandler(const QBluetoothUuid &service, const ComPtr<IGattDeviceService2> &deviceService)
95 : mService(service)
96 , mDeviceService(deviceService)
97 {
98 qCDebug(QT_BT_WINRT) << __FUNCTION__;
99 }
100
101 ~QWinRTLowEnergyServiceHandler()
102 {
103 }
104
105public slots:
106 void obtainCharList()
107 {
108 QVector<QBluetoothUuid> indicateChars;
109 quint16 startHandle = 0;
110 quint16 endHandle = 0;
111 qCDebug(QT_BT_WINRT) << __FUNCTION__;
112 ComPtr<IVectorView<GattCharacteristic *>> characteristics;
113 HRESULT hr = mDeviceService->GetAllCharacteristics(&characteristics);
114 Q_ASSERT_SUCCEEDED(hr);
115 if (!characteristics) {
116 emit charListObtained(mService, mCharacteristicList, indicateChars, startHandle, endHandle);
117 QThread::currentThread()->quit();
118 return;
119 }
120
121 uint characteristicsCount;
122 hr = characteristics->get_Size(&characteristicsCount);
123 Q_ASSERT_SUCCEEDED(hr);
124 for (uint i = 0; i < characteristicsCount; ++i) {
125 ComPtr<IGattCharacteristic> characteristic;
126 hr = characteristics->GetAt(i, &characteristic);
127 Q_ASSERT_SUCCEEDED(hr);
128 quint16 handle;
129 hr = characteristic->get_AttributeHandle(&handle);
130 Q_ASSERT_SUCCEEDED(hr);
131 QLowEnergyServicePrivate::CharData charData;
132 charData.valueHandle = handle + 1;
133 if (startHandle == 0 || startHandle > handle)
134 startHandle = handle;
135 if (endHandle == 0 || endHandle < handle)
136 endHandle = handle;
137 GUID guuid;
138 hr = characteristic->get_Uuid(&guuid);
139 Q_ASSERT_SUCCEEDED(hr);
140 charData.uuid = QBluetoothUuid(guuid);
141 GattCharacteristicProperties properties;
142 hr = characteristic->get_CharacteristicProperties(&properties);
143 Q_ASSERT_SUCCEEDED(hr);
144 charData.properties = QLowEnergyCharacteristic::PropertyTypes(properties & 0xff);
145 if (charData.properties & QLowEnergyCharacteristic::Read) {
146 ComPtr<IAsyncOperation<GattReadResult *>> readOp;
147 hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp);
148 Q_ASSERT_SUCCEEDED(hr);
149 ComPtr<IGattReadResult> readResult;
150 hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf());
151 Q_ASSERT_SUCCEEDED(hr);
152 if (readResult)
153 charData.value = byteArrayFromGattResult(readResult);
154 }
155 ComPtr<IGattCharacteristic2> characteristic2;
156 hr = characteristic.As(&characteristic2);
157 Q_ASSERT_SUCCEEDED(hr);
158 ComPtr<IVectorView<GattDescriptor *>> descriptors;
159 hr = characteristic2->GetAllDescriptors(&descriptors);
160 Q_ASSERT_SUCCEEDED(hr);
161 uint descriptorCount;
162 hr = descriptors->get_Size(&descriptorCount);
163 Q_ASSERT_SUCCEEDED(hr);
164 for (uint j = 0; j < descriptorCount; ++j) {
165 QLowEnergyServicePrivate::DescData descData;
166 ComPtr<IGattDescriptor> descriptor;
167 hr = descriptors->GetAt(j, &descriptor);
168 Q_ASSERT_SUCCEEDED(hr);
169 quint16 descHandle;
170 hr = descriptor->get_AttributeHandle(&descHandle);
171 Q_ASSERT_SUCCEEDED(hr);
172 GUID descriptorUuid;
173 hr = descriptor->get_Uuid(&descriptorUuid);
174 Q_ASSERT_SUCCEEDED(hr);
175 descData.uuid = QBluetoothUuid(descriptorUuid);
176 if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) {
177 ComPtr<IAsyncOperation<ClientCharConfigDescriptorResult *>> readOp;
178 hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp);
179 Q_ASSERT_SUCCEEDED(hr);
180 ComPtr<IClientCharConfigDescriptorResult> readResult;
181 hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf());
182 Q_ASSERT_SUCCEEDED(hr);
183 GattClientCharacteristicConfigurationDescriptorValue value;
184 hr = readResult->get_ClientCharacteristicConfigurationDescriptor(&value);
185 Q_ASSERT_SUCCEEDED(hr);
186 quint16 result = 0;
187 bool correct = false;
188 if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) {
189 result |= GattClientCharacteristicConfigurationDescriptorValue_Indicate;
190 correct = true;
191 }
192 if (value & GattClientCharacteristicConfigurationDescriptorValue_Notify) {
193 result |= GattClientCharacteristicConfigurationDescriptorValue_Notify;
194 correct = true;
195 }
196 if (value == GattClientCharacteristicConfigurationDescriptorValue_None) {
197 correct = true;
198 }
199 if (!correct)
200 continue;
201
202 descData.value = QByteArray(2, Qt::Uninitialized);
203 qToLittleEndian(result, descData.value.data());
204 indicateChars << charData.uuid;
205 } else {
206 ComPtr<IAsyncOperation<GattReadResult *>> readOp;
207 hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp);
208 Q_ASSERT_SUCCEEDED(hr);
209 ComPtr<IGattReadResult> readResult;
210 hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf());
211 Q_ASSERT_SUCCEEDED(hr);
212 if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription)
213 descData.value = byteArrayFromGattResult(readResult, true);
214 else
215 descData.value = byteArrayFromGattResult(readResult);
216 }
217 charData.descriptorList.insert(descHandle, descData);
218 }
219 mCharacteristicList.insert(handle, charData);
220 }
221 emit charListObtained(mService, mCharacteristicList, indicateChars, startHandle, endHandle);
222 QThread::currentThread()->quit();
223 }
224
225public:
226 QBluetoothUuid mService;
227 ComPtr<IGattDeviceService2> mDeviceService;
228 QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData> mCharacteristicList;
229
230signals:
231 void charListObtained(const QBluetoothUuid &service, QHash<QLowEnergyHandle,
232 QLowEnergyServicePrivate::CharData> charList,
233 QVector<QBluetoothUuid> indicateChars,
234 QLowEnergyHandle startHandle, QLowEnergyHandle endHandle);
235};
236
237QLowEnergyControllerPrivateWinRT::QLowEnergyControllerPrivateWinRT()
238 : QLowEnergyControllerPrivate()
239{
240 qCDebug(QT_BT_WINRT) << __FUNCTION__;
241
242 registerQLowEnergyControllerMetaType();
243 connect(this, &QLowEnergyControllerPrivateWinRT::characteristicChanged,
244 this, &QLowEnergyControllerPrivateWinRT::handleCharacteristicChanged,
245 Qt::QueuedConnection);
246}
247
248QLowEnergyControllerPrivateWinRT::~QLowEnergyControllerPrivateWinRT()
249{
250 if (mDevice && mStatusChangedToken.value)
251 mDevice->remove_ConnectionStatusChanged(mStatusChangedToken);
252
253 unregisterFromValueChanges();
254}
255
256void QLowEnergyControllerPrivateWinRT::init()
257{
258}
259
260void QLowEnergyControllerPrivateWinRT::connectToDevice()
261{
262 qCDebug(QT_BT_WINRT) << __FUNCTION__;
263 Q_Q(QLowEnergyController);
264 if (remoteDevice.isNull()) {
265 qWarning() << "Invalid/null remote device address";
266 setError(QLowEnergyController::UnknownRemoteDeviceError);
267 return;
268 }
269
270 setState(QLowEnergyController::ConnectingState);
271
272 ComPtr<IBluetoothLEDeviceStatics> deviceStatics;
273 HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice).Get(), &deviceStatics);
274 Q_ASSERT_SUCCEEDED(hr);
275 ComPtr<IAsyncOperation<BluetoothLEDevice *>> deviceFromIdOperation;
276 hr = deviceStatics->FromBluetoothAddressAsync(remoteDevice.toUInt64(), &deviceFromIdOperation);
277 Q_ASSERT_SUCCEEDED(hr);
278 hr = QWinRTFunctions::await(deviceFromIdOperation, mDevice.GetAddressOf());
279 Q_ASSERT_SUCCEEDED(hr);
280
281 if (!mDevice) {
282 qCDebug(QT_BT_WINRT) << "Could not find LE device";
283 setError(QLowEnergyController::InvalidBluetoothAdapterError);
284 setState(QLowEnergyController::UnconnectedState);
285 }
286 BluetoothConnectionStatus status;
287 hr = mDevice->get_ConnectionStatus(&status);
288 Q_ASSERT_SUCCEEDED(hr);
289 hr = QEventDispatcherWinRT::runOnXamlThread([this, q]() {
290 HRESULT hr;
291 hr = mDevice->add_ConnectionStatusChanged(Callback<StatusHandler>([this, q](IBluetoothLEDevice *dev, IInspectable *) {
292 BluetoothConnectionStatus status;
293 HRESULT hr;
294 hr = dev->get_ConnectionStatus(&status);
295 Q_ASSERT_SUCCEEDED(hr);
296 if (state == QLowEnergyController::ConnectingState
297 && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) {
298 setState(QLowEnergyController::ConnectedState);
299 emit q->connected();
300 } else if (state != QLowEnergyController::UnconnectedState
301 && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) {
302 invalidateServices();
303 unregisterFromValueChanges();
304 setError(QLowEnergyController::RemoteHostClosedError);
305 setState(QLowEnergyController::UnconnectedState);
306 emit q->disconnected();
307 }
308 return S_OK;
309 }).Get(), &mStatusChangedToken);
310 Q_ASSERT_SUCCEEDED(hr);
311 return S_OK;
312 });
313 Q_ASSERT_SUCCEEDED(hr);
314
315 if (status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) {
316 setState(QLowEnergyController::ConnectedState);
317 emit q->connected();
318 return;
319 }
320
321 ComPtr<IVectorView <GattDeviceService *>> deviceServices;
322 hr = mDevice->get_GattServices(&deviceServices);
323 Q_ASSERT_SUCCEEDED(hr);
324 uint serviceCount;
325 hr = deviceServices->get_Size(&serviceCount);
326 Q_ASSERT_SUCCEEDED(hr);
327 // Windows Phone automatically connects to the device as soon as a service value is read/written.
328 // Thus we read one value in order to establish the connection.
329 for (uint i = 0; i < serviceCount; ++i) {
330 ComPtr<IGattDeviceService> service;
331 hr = deviceServices->GetAt(i, &service);
332 Q_ASSERT_SUCCEEDED(hr);
333 ComPtr<IGattDeviceService2> service2;
334 hr = service.As(&service2);
335 Q_ASSERT_SUCCEEDED(hr);
336 ComPtr<IVectorView<GattCharacteristic *>> characteristics;
337 hr = service2->GetAllCharacteristics(&characteristics);
338 if (hr == E_ACCESSDENIED) {
339 // Everything will work as expected up until this point if the manifest capabilties
340 // for bluetooth LE are not set.
341 qCWarning(QT_BT_WINRT) << "Could not obtain characteristic list. Please check your "
342 "manifest capabilities";
343 setState(QLowEnergyController::UnconnectedState);
344 setError(QLowEnergyController::ConnectionError);
345 return;
346 } else {
347 Q_ASSERT_SUCCEEDED(hr);
348 }
349 uint characteristicsCount;
350 hr = characteristics->get_Size(&characteristicsCount);
351 Q_ASSERT_SUCCEEDED(hr);
352 for (uint j = 0; j < characteristicsCount; ++j) {
353 ComPtr<IGattCharacteristic> characteristic;
354 hr = characteristics->GetAt(j, &characteristic);
355 Q_ASSERT_SUCCEEDED(hr);
356 ComPtr<IAsyncOperation<GattReadResult *>> op;
357 GattCharacteristicProperties props;
358 hr = characteristic->get_CharacteristicProperties(&props);
359 Q_ASSERT_SUCCEEDED(hr);
360 if (!(props & GattCharacteristicProperties_Read))
361 continue;
362 hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode::BluetoothCacheMode_Uncached, &op);
363 Q_ASSERT_SUCCEEDED(hr);
364 ComPtr<IGattReadResult> result;
365 hr = QWinRTFunctions::await(op, result.GetAddressOf());
366 if (hr == E_INVALIDARG) {
367 // E_INVALIDARG happens when user tries to connect to a device that was paired
368 // before but is not available.
369 qCDebug(QT_BT_WINRT) << "Could not obtain characteristic read result that triggers"
370 "device connection. Is the device reachable?";
371 setError(QLowEnergyController::ConnectionError);
372 setState(QLowEnergyController::UnconnectedState);
373 return;
374 } else if (hr != S_OK) {
375 qCWarning(QT_BT_WINRT) << "Connecting to device failed: "
376 << qt_error_string(hr);
377 setError(QLowEnergyController::ConnectionError);
378 setState(QLowEnergyController::UnconnectedState);
379 return;
380 }
381 ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
382 hr = result->get_Value(&buffer);
383 Q_ASSERT_SUCCEEDED(hr);
384 if (!buffer) {
385 qCDebug(QT_BT_WINRT) << "Problem reading value";
386 setError(QLowEnergyController::ConnectionError);
387 setState(QLowEnergyController::UnconnectedState);
388 }
389 return;
390 }
391 }
392}
393
394void QLowEnergyControllerPrivateWinRT::disconnectFromDevice()
395{
396 qCDebug(QT_BT_WINRT) << __FUNCTION__;
397 Q_Q(QLowEnergyController);
398 setState(QLowEnergyController::ClosingState);
399 unregisterFromValueChanges();
400 if (mDevice) {
401 if (mStatusChangedToken.value) {
402 mDevice->remove_ConnectionStatusChanged(mStatusChangedToken);
403 mStatusChangedToken.value = 0;
404 }
405 mDevice = nullptr;
406 }
407 setState(QLowEnergyController::UnconnectedState);
408 emit q->disconnected();
409}
410
411ComPtr<IGattDeviceService> QLowEnergyControllerPrivateWinRT::getNativeService(const QBluetoothUuid &serviceUuid)
412{
413 ComPtr<IGattDeviceService> deviceService;
414 HRESULT hr;
415 hr = mDevice->GetGattService(serviceUuid, &deviceService);
416 if (FAILED(hr))
417 qCDebug(QT_BT_WINRT) << "Could not obtain native service for Uuid" << serviceUuid;
418 return deviceService;
419}
420
421ComPtr<IGattCharacteristic> QLowEnergyControllerPrivateWinRT::getNativeCharacteristic(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid)
422{
423 ComPtr<IGattDeviceService> service = getNativeService(serviceUuid);
424 if (!service)
425 return nullptr;
426
427 ComPtr<IVectorView<GattCharacteristic *>> characteristics;
428 HRESULT hr = service->GetCharacteristics(charUuid, &characteristics);
429 RETURN_IF_FAILED("Could not obtain native characteristics for service", return nullptr);
430 ComPtr<IGattCharacteristic> characteristic;
431 hr = characteristics->GetAt(0, &characteristic);
432 RETURN_IF_FAILED("Could not obtain first characteristic for service", return nullptr);
433 return characteristic;
434}
435
436void QLowEnergyControllerPrivateWinRT::registerForValueChanges(const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid)
437{
438 qCDebug(QT_BT_WINRT) << "Registering characteristic" << charUuid << "in service"
439 << serviceUuid << "for value changes";
440 for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens)) {
441 GUID guuid;
442 HRESULT hr;
443 hr = entry.characteristic->get_Uuid(&guuid);
444 Q_ASSERT_SUCCEEDED(hr);
445 if (QBluetoothUuid(guuid) == charUuid)
446 return;
447 }
448 ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(serviceUuid, charUuid);
449
450 EventRegistrationToken token;
451 HRESULT hr;
452 hr = characteristic->add_ValueChanged(Callback<ValueChangedHandler>([this](IGattCharacteristic *characteristic, IGattValueChangedEventArgs *args) {
453 HRESULT hr;
454 quint16 handle;
455 hr = characteristic->get_AttributeHandle(&handle);
456 Q_ASSERT_SUCCEEDED(hr);
457 ComPtr<IBuffer> buffer;
458 hr = args->get_CharacteristicValue(&buffer);
459 Q_ASSERT_SUCCEEDED(hr);
460 emit characteristicChanged(handle, byteArrayFromBuffer(buffer));
461 return S_OK;
462 }).Get(), &token);
463 Q_ASSERT_SUCCEEDED(hr);
464 mValueChangedTokens.append(ValueChangedEntry(characteristic, token));
465 qCDebug(QT_BT_WINRT) << "Characteristic" << charUuid << "in service"
466 << serviceUuid << "registered for value changes";
467}
468
469void QLowEnergyControllerPrivateWinRT::unregisterFromValueChanges()
470{
471 qCDebug(QT_BT_WINRT) << "Unregistering " << mValueChangedTokens.count() << " value change tokens";
472 HRESULT hr;
473 for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens)) {
474 hr = entry.characteristic->remove_ValueChanged(entry.token);
475 Q_ASSERT_SUCCEEDED(hr);
476 }
477 mValueChangedTokens.clear();
478}
479
480void QLowEnergyControllerPrivateWinRT::obtainIncludedServices(QSharedPointer<QLowEnergyServicePrivate> servicePointer,
481 ComPtr<IGattDeviceService> service)
482{
483 Q_Q(QLowEnergyController);
484 ComPtr<IGattDeviceService2> service2;
485 HRESULT hr = service.As(&service2);
486 Q_ASSERT_SUCCEEDED(hr);
487 ComPtr<IVectorView<GattDeviceService *>> includedServices;
488 hr = service2->GetAllIncludedServices(&includedServices);
489 // Some devices return ERROR_ACCESS_DISABLED_BY_POLICY
490 if (FAILED(hr))
491 return;
492
493 uint count;
494 hr = includedServices->get_Size(&count);
495 Q_ASSERT_SUCCEEDED(hr);
496 for (uint i = 0; i < count; ++i) {
497 ComPtr<IGattDeviceService> includedService;
498 hr = includedServices->GetAt(i, &includedService);
499 Q_ASSERT_SUCCEEDED(hr);
500 GUID guuid;
501 hr = includedService->get_Uuid(&guuid);
502 Q_ASSERT_SUCCEEDED(hr);
503 const QBluetoothUuid includedUuid(guuid);
504 QSharedPointer<QLowEnergyServicePrivate> includedPointer;
505 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__
506 << "Changing service pointer from thread"
507 << QThread::currentThread();
508 if (serviceList.contains(includedUuid)) {
509 includedPointer = serviceList.value(includedUuid);
510 } else {
511 QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate();
512 priv->uuid = includedUuid;
513 priv->setController(this);
514
515 includedPointer = QSharedPointer<QLowEnergyServicePrivate>(priv);
516 serviceList.insert(includedUuid, includedPointer);
517 }
518 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__
519 << "Changing service pointer from thread"
520 << QThread::currentThread();
521 includedPointer->type |= QLowEnergyService::IncludedService;
522 servicePointer->includedServices.append(includedUuid);
523
524 obtainIncludedServices(includedPointer, includedService);
525
526 emit q->serviceDiscovered(includedUuid);
527 }
528}
529
530void QLowEnergyControllerPrivateWinRT::discoverServices()
531{
532 Q_Q(QLowEnergyController);
533
534 qCDebug(QT_BT_WINRT) << "Service discovery initiated";
535 ComPtr<IVectorView<GattDeviceService *>> deviceServices;
536 HRESULT hr = mDevice->get_GattServices(&deviceServices);
537 Q_ASSERT_SUCCEEDED(hr);
538 uint serviceCount;
539 hr = deviceServices->get_Size(&serviceCount);
540 Q_ASSERT_SUCCEEDED(hr);
541 for (uint i = 0; i < serviceCount; ++i) {
542 ComPtr<IGattDeviceService> deviceService;
543 hr = deviceServices->GetAt(i, &deviceService);
544 Q_ASSERT_SUCCEEDED(hr);
545 GUID guuid;
546 hr = deviceService->get_Uuid(&guuid);
547 Q_ASSERT_SUCCEEDED(hr);
548 const QBluetoothUuid service(guuid);
549
550 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__
551 << "Changing service pointer from thread"
552 << QThread::currentThread();
553 QSharedPointer<QLowEnergyServicePrivate> pointer;
554 if (serviceList.contains(service)) {
555 pointer = serviceList.value(service);
556 } else {
557 QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate();
558 priv->uuid = service;
559 priv->setController(this);
560
561 pointer = QSharedPointer<QLowEnergyServicePrivate>(priv);
562 serviceList.insert(service, pointer);
563 }
564 pointer->type |= QLowEnergyService::PrimaryService;
565
566 obtainIncludedServices(pointer, deviceService);
567
568 emit q->serviceDiscovered(service);
569 }
570
571 setState(QLowEnergyController::DiscoveredState);
572 emit q->discoveryFinished();
573}
574
575void QLowEnergyControllerPrivateWinRT::discoverServiceDetails(const QBluetoothUuid &service)
576{
577 qCDebug(QT_BT_WINRT) << __FUNCTION__ << service;
578 if (!serviceList.contains(service)) {
579 qCWarning(QT_BT_WINRT) << "Discovery done of unknown service:"
580 << service.toString();
581 return;
582 }
583
584 ComPtr<IGattDeviceService> deviceService = getNativeService(service);
585 if (!deviceService) {
586 qCDebug(QT_BT_WINRT) << "Could not obtain native service for uuid " << service;
587 return;
588 }
589
590 //update service data
591 QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
592 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
593 << QThread::currentThread();
594
595 pointer->setState(QLowEnergyService::DiscoveringServices);
596 ComPtr<IGattDeviceService2> deviceService2;
597 HRESULT hr = deviceService.As(&deviceService2);
598 Q_ASSERT_SUCCEEDED(hr);
599 ComPtr<IVectorView<GattDeviceService *>> deviceServices;
600 hr = deviceService2->GetAllIncludedServices(&deviceServices);
601 if (FAILED(hr)) { // ERROR_ACCESS_DISABLED_BY_POLICY
602 qCDebug(QT_BT_WINRT) << "Could not obtain included services list for" << service;
603 pointer->setError(QLowEnergyService::UnknownError);
604 pointer->setState(QLowEnergyService::InvalidService);
605 return;
606 }
607 uint serviceCount;
608 hr = deviceServices->get_Size(&serviceCount);
609 Q_ASSERT_SUCCEEDED(hr);
610 for (uint i = 0; i < serviceCount; ++i) {
611 ComPtr<IGattDeviceService> includedService;
612 hr = deviceServices->GetAt(i, &includedService);
613 Q_ASSERT_SUCCEEDED(hr);
614 GUID guuid;
615 hr = includedService->get_Uuid(&guuid);
616 Q_ASSERT_SUCCEEDED(hr);
617
618 const QBluetoothUuid service(guuid);
619 if (service.isNull()) {
620 qCDebug(QT_BT_WINRT) << "Could not find service";
621 return;
622 }
623
624 pointer->includedServices.append(service);
625
626 // update the type of the included service
627 QSharedPointer<QLowEnergyServicePrivate> otherService = serviceList.value(service);
628 if (!otherService.isNull())
629 otherService->type |= QLowEnergyService::IncludedService;
630 }
631
632 QWinRTLowEnergyServiceHandler *worker = new QWinRTLowEnergyServiceHandler(service, deviceService2);
633 QThread *thread = new QThread;
634 worker->moveToThread(thread);
635 connect(thread, &QThread::started, worker, &QWinRTLowEnergyServiceHandler::obtainCharList);
636 connect(thread, &QThread::finished, thread, &QObject::deleteLater);
637 connect(thread, &QThread::finished, worker, &QObject::deleteLater);
638 connect(worker, &QWinRTLowEnergyServiceHandler::charListObtained,
639 [this, thread](const QBluetoothUuid &service, QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData> charList
640 , QVector<QBluetoothUuid> indicateChars
641 , QLowEnergyHandle startHandle, QLowEnergyHandle endHandle) {
642 if (!serviceList.contains(service)) {
643 qCWarning(QT_BT_WINRT) << "Discovery done of unknown service:"
644 << service.toString();
645 return;
646 }
647
648 QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service);
649 pointer->startHandle = startHandle;
650 pointer->endHandle = endHandle;
651 pointer->characteristicList = charList;
652
653 HRESULT hr;
654 hr = QEventDispatcherWinRT::runOnXamlThread([indicateChars, service, this]() {
655 for (const QBluetoothUuid &indicateChar : qAsConst(indicateChars))
656 registerForValueChanges(service, indicateChar);
657 return S_OK;
658 });
659 Q_ASSERT_SUCCEEDED(hr);
660
661 pointer->setState(QLowEnergyService::ServiceDiscovered);
662 thread->exit(0);
663 });
664 thread->start();
665}
666
667void QLowEnergyControllerPrivateWinRT::startAdvertising(const QLowEnergyAdvertisingParameters &, const QLowEnergyAdvertisingData &, const QLowEnergyAdvertisingData &)
668{
669 setError(QLowEnergyController::AdvertisingError);
670 Q_UNIMPLEMENTED();
671}
672
673void QLowEnergyControllerPrivateWinRT::stopAdvertising()
674{
675 Q_UNIMPLEMENTED();
676}
677
678void QLowEnergyControllerPrivateWinRT::requestConnectionUpdate(const QLowEnergyConnectionParameters &)
679{
680 Q_UNIMPLEMENTED();
681}
682
683void QLowEnergyControllerPrivateWinRT::readCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
684 const QLowEnergyHandle charHandle)
685{
686 qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle;
687 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
688 << QThread::currentThread();
689 Q_ASSERT(!service.isNull());
690 if (role == QLowEnergyController::PeripheralRole) {
691 service->setError(QLowEnergyService::CharacteristicReadError);
692 Q_UNIMPLEMENTED();
693 return;
694 }
695
696 if (!service->characteristicList.contains(charHandle)) {
697 qCDebug(QT_BT_WINRT) << charHandle << "could not be found in service" << service->uuid;
698 service->setError(QLowEnergyService::CharacteristicReadError);
699 return;
700 }
701
702 HRESULT hr;
703 hr = QEventDispatcherWinRT::runOnXamlThread([charHandle, service, this]() {
704 const QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
705 if (!(charData.properties & QLowEnergyCharacteristic::Read))
706 qCDebug(QT_BT_WINRT) << "Read flag is not set for characteristic" << charData.uuid;
707
708 ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, charData.uuid);
709 if (!characteristic) {
710 qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid
711 << "from service" << service->uuid;
712 service->setError(QLowEnergyService::CharacteristicReadError);
713 return S_OK;
714 }
715 ComPtr<IAsyncOperation<GattReadResult*>> readOp;
716 HRESULT hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp);
717 Q_ASSERT_SUCCEEDED(hr);
718 auto readCompletedLambda = [charData, charHandle, service]
719 (IAsyncOperation<GattReadResult*> *op, AsyncStatus status)
720 {
721 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
722 qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "read operation failed.";
723 service->setError(QLowEnergyService::CharacteristicReadError);
724 return S_OK;
725 }
726 ComPtr<IGattReadResult> characteristicValue;
727 HRESULT hr;
728 hr = op->GetResults(&characteristicValue);
729 if (FAILED(hr)) {
730 qCDebug(QT_BT_WINRT) << "Could not obtain result for characteristic" << charHandle;
731 service->setError(QLowEnergyService::CharacteristicReadError);
732 return S_OK;
733 }
734
735 const QByteArray value = byteArrayFromGattResult(characteristicValue);
736 QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
737 charData.value = value;
738 service->characteristicList.insert(charHandle, charData);
739 emit service->characteristicRead(QLowEnergyCharacteristic(service, charHandle), value);
740 return S_OK;
741 };
742 hr = readOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattReadResult *>>(readCompletedLambda).Get());
743 Q_ASSERT_SUCCEEDED(hr);
744 return S_OK;
745 });
746 Q_ASSERT_SUCCEEDED(hr);
747}
748
749void QLowEnergyControllerPrivateWinRT::readDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,
750 const QLowEnergyHandle charHandle,
751 const QLowEnergyHandle descHandle)
752{
753 qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << descHandle;
754 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
755 << QThread::currentThread();
756 Q_ASSERT(!service.isNull());
757 if (role == QLowEnergyController::PeripheralRole) {
758 service->setError(QLowEnergyService::DescriptorReadError);
759 Q_UNIMPLEMENTED();
760 return;
761 }
762
763 if (!service->characteristicList.contains(charHandle)) {
764 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "in characteristic" << charHandle
765 << "cannot be found in service" << service->uuid;
766 service->setError(QLowEnergyService::DescriptorReadError);
767 return;
768 }
769
770 HRESULT hr;
771 hr = QEventDispatcherWinRT::runOnXamlThread([charHandle, descHandle, service, this]() {
772 const QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
773 ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, charData.uuid);
774 if (!characteristic) {
775 qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid
776 << "from service" << service->uuid;
777 service->setError(QLowEnergyService::DescriptorReadError);
778 return S_OK;
779 }
780
781 // Get native descriptor
782 if (!charData.descriptorList.contains(descHandle))
783 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "cannot be found in characteristic" << charHandle;
784 const QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle);
785 const QBluetoothUuid descUuid = descData.uuid;
786 if (descUuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) {
787 ComPtr<IAsyncOperation<ClientCharConfigDescriptorResult *>> readOp;
788 HRESULT hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp);
789 Q_ASSERT_SUCCEEDED(hr);
790 auto readCompletedLambda = [charHandle, descHandle, service]
791 (IAsyncOperation<ClientCharConfigDescriptorResult *> *op, AsyncStatus status)
792 {
793 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
794 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "read operation failed";
795 service->setError(QLowEnergyService::DescriptorReadError);
796 return S_OK;
797 }
798 ComPtr<IClientCharConfigDescriptorResult> iValue;
799 HRESULT hr;
800 hr = op->GetResults(&iValue);
801 if (FAILED(hr)) {
802 qCDebug(QT_BT_WINRT) << "Could not obtain result for descriptor" << descHandle;
803 service->setError(QLowEnergyService::DescriptorReadError);
804 return S_OK;
805 }
806 GattClientCharacteristicConfigurationDescriptorValue value;
807 hr = iValue->get_ClientCharacteristicConfigurationDescriptor(&value);
808 if (FAILED(hr)) {
809 qCDebug(QT_BT_WINRT) << "Could not obtain value for descriptor" << descHandle;
810 service->setError(QLowEnergyService::DescriptorReadError);
811 return S_OK;
812 }
813 quint16 result = 0;
814 bool correct = false;
815 if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) {
816 result |= QLowEnergyCharacteristic::Indicate;
817 correct = true;
818 }
819 if (value & GattClientCharacteristicConfigurationDescriptorValue_Notify) {
820 result |= QLowEnergyCharacteristic::Notify;
821 correct = true;
822 }
823 if (value == GattClientCharacteristicConfigurationDescriptorValue_None)
824 correct = true;
825 if (!correct) {
826 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle
827 << "read operation failed. Obtained unexpected value.";
828 service->setError(QLowEnergyService::DescriptorReadError);
829 return S_OK;
830 }
831 QLowEnergyServicePrivate::DescData descData;
832 descData.uuid = QBluetoothUuid::ClientCharacteristicConfiguration;
833 descData.value = QByteArray(2, Qt::Uninitialized);
834 qToLittleEndian(result, descData.value.data());
835 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
836 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
837 descData.value);
838 return S_OK;
839 };
840 hr = readOp->put_Completed(Callback<IAsyncOperationCompletedHandler<ClientCharConfigDescriptorResult *>>(readCompletedLambda).Get());
841 Q_ASSERT_SUCCEEDED(hr);
842 return S_OK;
843 } else {
844 ComPtr<IVectorView<GattDescriptor *>> descriptors;
845 HRESULT hr = characteristic->GetDescriptors(descData.uuid, &descriptors);
846 Q_ASSERT_SUCCEEDED(hr);
847 ComPtr<IGattDescriptor> descriptor;
848 hr = descriptors->GetAt(0, &descriptor);
849 Q_ASSERT_SUCCEEDED(hr);
850 ComPtr<IAsyncOperation<GattReadResult*>> readOp;
851 hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp);
852 Q_ASSERT_SUCCEEDED(hr);
853 auto readCompletedLambda = [charHandle, descHandle, descUuid, service]
854 (IAsyncOperation<GattReadResult*> *op, AsyncStatus status)
855 {
856 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
857 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "read operation failed";
858 service->setError(QLowEnergyService::DescriptorReadError);
859 return S_OK;
860 }
861 ComPtr<IGattReadResult> descriptorValue;
862 HRESULT hr;
863 hr = op->GetResults(&descriptorValue);
864 if (FAILED(hr)) {
865 qCDebug(QT_BT_WINRT) << "Could not obtain result for descriptor" << descHandle;
866 service->setError(QLowEnergyService::DescriptorReadError);
867 return S_OK;
868 }
869 QLowEnergyServicePrivate::DescData descData;
870 if (descUuid == QBluetoothUuid::CharacteristicUserDescription)
871 descData.value = byteArrayFromGattResult(descriptorValue, true);
872 else
873 descData.value = byteArrayFromGattResult(descriptorValue);
874 service->characteristicList[charHandle].descriptorList[descHandle] = descData;
875 emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle),
876 descData.value);
877 return S_OK;
878 };
879 hr = readOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattReadResult *>>(readCompletedLambda).Get());
880 return S_OK;
881 }
882 });
883 Q_ASSERT_SUCCEEDED(hr);
884}
885
886void QLowEnergyControllerPrivateWinRT::writeCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
887 const QLowEnergyHandle charHandle,
888 const QByteArray &newValue,
889 QLowEnergyService::WriteMode mode)
890{
891 qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << newValue << mode;
892 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
893 << QThread::currentThread();
894 Q_ASSERT(!service.isNull());
895 if (role == QLowEnergyController::PeripheralRole) {
896 service->setError(QLowEnergyService::CharacteristicWriteError);
897 Q_UNIMPLEMENTED();
898 return;
899 }
900 if (!service->characteristicList.contains(charHandle)) {
901 qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "cannot be found in service" << service->uuid;
902 service->setError(QLowEnergyService::CharacteristicWriteError);
903 return;
904 }
905
906 QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
907 const bool writeWithResponse = mode == QLowEnergyService::WriteWithResponse;
908 if (!(charData.properties & (writeWithResponse ? QLowEnergyCharacteristic::Write : QLowEnergyCharacteristic::WriteNoResponse)))
909 qCDebug(QT_BT_WINRT) << "Write flag is not set for characteristic" << charHandle;
910
911 HRESULT hr;
912 hr = QEventDispatcherWinRT::runOnXamlThread([charData, charHandle, this, service, newValue, writeWithResponse]() {
913 ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, charData.uuid);
914 if (!characteristic) {
915 qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid
916 << "from service" << service->uuid;
917 service->setError(QLowEnergyService::CharacteristicWriteError);
918 return S_OK;
919 }
920 ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory;
921 HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), &bufferFactory);
922 Q_ASSERT_SUCCEEDED(hr);
923 ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
924 const int length = newValue.length();
925 hr = bufferFactory->Create(length, &buffer);
926 Q_ASSERT_SUCCEEDED(hr);
927 hr = buffer->put_Length(length);
928 Q_ASSERT_SUCCEEDED(hr);
929 ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess;
930 hr = buffer.As(&byteAccess);
931 Q_ASSERT_SUCCEEDED(hr);
932 byte *bytes;
933 hr = byteAccess->Buffer(&bytes);
934 Q_ASSERT_SUCCEEDED(hr);
935 memcpy(bytes, newValue, length);
936 ComPtr<IAsyncOperation<GattCommunicationStatus>> writeOp;
937 GattWriteOption option = writeWithResponse ? GattWriteOption_WriteWithResponse : GattWriteOption_WriteWithoutResponse;
938 hr = characteristic->WriteValueWithOptionAsync(buffer.Get(), option, &writeOp);
939 Q_ASSERT_SUCCEEDED(hr);
940 auto writeCompletedLambda =[charData, charHandle, newValue, service, writeWithResponse, this]
941 (IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status)
942 {
943 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
944 qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "write operation failed";
945 service->setError(QLowEnergyService::CharacteristicWriteError);
946 return S_OK;
947 }
948 GattCommunicationStatus result;
949 HRESULT hr;
950 hr = op->GetResults(&result);
951 if (hr == E_BLUETOOTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH) {
952 qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "write operation was tried with invalid value length";
953 service->setError(QLowEnergyService::CharacteristicWriteError);
954 return S_OK;
955 }
956 Q_ASSERT_SUCCEEDED(hr);
957 if (result != GattCommunicationStatus_Success) {
958 qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "write operation failed";
959 service->setError(QLowEnergyService::CharacteristicWriteError);
960 return S_OK;
961 }
962 // only update cache when property is readable. Otherwise it remains
963 // empty.
964 if (charData.properties & QLowEnergyCharacteristic::Read)
965 updateValueOfCharacteristic(charHandle, newValue, false);
966 if (writeWithResponse)
967 emit service->characteristicWritten(QLowEnergyCharacteristic(service, charHandle), newValue);
968 return S_OK;
969 };
970 hr = writeOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus>>(writeCompletedLambda).Get());
971 Q_ASSERT_SUCCEEDED(hr);
972 return S_OK;
973 });
974 Q_ASSERT_SUCCEEDED(hr);
975}
976
977void QLowEnergyControllerPrivateWinRT::writeDescriptor(
978 const QSharedPointer<QLowEnergyServicePrivate> service,
979 const QLowEnergyHandle charHandle,
980 const QLowEnergyHandle descHandle,
981 const QByteArray &newValue)
982{
983 qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << descHandle << newValue;
984 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
985 << QThread::currentThread();
986 Q_ASSERT(!service.isNull());
987 if (role == QLowEnergyController::PeripheralRole) {
988 service->setError(QLowEnergyService::DescriptorWriteError);
989 Q_UNIMPLEMENTED();
990 return;
991 }
992
993 if (!service->characteristicList.contains(charHandle)) {
994 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "in characteristic" << charHandle
995 << "could not be found in service" << service->uuid;
996 service->setError(QLowEnergyService::DescriptorWriteError);
997 return;
998 }
999
1000 HRESULT hr;
1001 hr = QEventDispatcherWinRT::runOnXamlThread([charHandle, descHandle, this, service, newValue]() {
1002 const QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle);
1003 ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, charData.uuid);
1004 if (!characteristic) {
1005 qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid
1006 << "from service" << service->uuid;
1007 service->setError(QLowEnergyService::DescriptorWriteError);
1008 return S_OK;
1009 }
1010
1011 // Get native descriptor
1012 if (!charData.descriptorList.contains(descHandle))
1013 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "could not be found in Characteristic" << charHandle;
1014
1015 QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle);
1016 if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) {
1017 GattClientCharacteristicConfigurationDescriptorValue value;
1018 quint16 intValue = qFromLittleEndian<quint16>(newValue);
1019 if (intValue & GattClientCharacteristicConfigurationDescriptorValue_Indicate && intValue & GattClientCharacteristicConfigurationDescriptorValue_Notify) {
1020 qCWarning(QT_BT_WINRT) << "Setting both Indicate and Notify is not supported on WinRT";
1021 value = (GattClientCharacteristicConfigurationDescriptorValue)(GattClientCharacteristicConfigurationDescriptorValue_Indicate | GattClientCharacteristicConfigurationDescriptorValue_Notify);
1022 } else if (intValue & GattClientCharacteristicConfigurationDescriptorValue_Indicate) {
1023 value = GattClientCharacteristicConfigurationDescriptorValue_Indicate;
1024 } else if (intValue & GattClientCharacteristicConfigurationDescriptorValue_Notify) {
1025 value = GattClientCharacteristicConfigurationDescriptorValue_Notify;
1026 } else if (intValue == 0) {
1027 value = GattClientCharacteristicConfigurationDescriptorValue_None;
1028 } else {
1029 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed: Invalid value";
1030 service->setError(QLowEnergyService::DescriptorWriteError);
1031 return S_OK;
1032 }
1033 ComPtr<IAsyncOperation<enum GattCommunicationStatus>> writeOp;
1034 HRESULT hr = characteristic->WriteClientCharacteristicConfigurationDescriptorAsync(value, &writeOp);
1035 Q_ASSERT_SUCCEEDED(hr);
1036 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, this]
1037 (IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status)
1038 {
1039 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
1040 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed";
1041 service->setError(QLowEnergyService::DescriptorWriteError);
1042 return S_OK;
1043 }
1044 GattCommunicationStatus result;
1045 HRESULT hr;
1046 hr = op->GetResults(&result);
1047 if (FAILED(hr)) {
1048 qCDebug(QT_BT_WINRT) << "Could not obtain result for descriptor" << descHandle;
1049 service->setError(QLowEnergyService::DescriptorWriteError);
1050 return S_OK;
1051 }
1052 if (result != GattCommunicationStatus_Success) {
1053 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed";
1054 service->setError(QLowEnergyService::DescriptorWriteError);
1055 return S_OK;
1056 }
1057 updateValueOfDescriptor(charHandle, descHandle, newValue, false);
1058 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle), newValue);
1059 return S_OK;
1060 };
1061 hr = writeOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus >>(writeCompletedLambda).Get());
1062 Q_ASSERT_SUCCEEDED(hr);
1063 } else {
1064 ComPtr<IVectorView<GattDescriptor *>> descriptors;
1065 HRESULT hr = characteristic->GetDescriptors(descData.uuid, &descriptors);
1066 Q_ASSERT_SUCCEEDED(hr);
1067 ComPtr<IGattDescriptor> descriptor;
1068 hr = descriptors->GetAt(0, &descriptor);
1069 Q_ASSERT_SUCCEEDED(hr);
1070 ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory;
1071 hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), &bufferFactory);
1072 Q_ASSERT_SUCCEEDED(hr);
1073 ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
1074 const int length = newValue.length();
1075 hr = bufferFactory->Create(length, &buffer);
1076 Q_ASSERT_SUCCEEDED(hr);
1077 hr = buffer->put_Length(length);
1078 Q_ASSERT_SUCCEEDED(hr);
1079 ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess;
1080 hr = buffer.As(&byteAccess);
1081 Q_ASSERT_SUCCEEDED(hr);
1082 byte *bytes;
1083 hr = byteAccess->Buffer(&bytes);
1084 Q_ASSERT_SUCCEEDED(hr);
1085 memcpy(bytes, newValue, length);
1086 ComPtr<IAsyncOperation<GattCommunicationStatus>> writeOp;
1087 hr = descriptor->WriteValueAsync(buffer.Get(), &writeOp);
1088 Q_ASSERT_SUCCEEDED(hr);
1089 auto writeCompletedLambda = [charHandle, descHandle, newValue, service, this]
1090 (IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status)
1091 {
1092 if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) {
1093 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed";
1094 service->setError(QLowEnergyService::DescriptorWriteError);
1095 return S_OK;
1096 }
1097 GattCommunicationStatus result;
1098 HRESULT hr;
1099 hr = op->GetResults(&result);
1100 if (FAILED(hr)) {
1101 qCDebug(QT_BT_WINRT) << "Could not obtain result for descriptor" << descHandle;
1102 service->setError(QLowEnergyService::DescriptorWriteError);
1103 return S_OK;
1104 }
1105 if (result != GattCommunicationStatus_Success) {
1106 qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed";
1107 service->setError(QLowEnergyService::DescriptorWriteError);
1108 return S_OK;
1109 }
1110 updateValueOfDescriptor(charHandle, descHandle, newValue, false);
1111 emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle), newValue);
1112 return S_OK;
1113 };
1114 hr = writeOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus>>(writeCompletedLambda).Get());
1115 Q_ASSERT_SUCCEEDED(hr);
1116 return S_OK;
1117 }
1118 return S_OK;
1119 });
1120 Q_ASSERT_SUCCEEDED(hr);
1121}
1122
1123
1124void QLowEnergyControllerPrivateWinRT::addToGenericAttributeList(const QLowEnergyServiceData &, QLowEnergyHandle)
1125{
1126 Q_UNIMPLEMENTED();
1127}
1128
1129void QLowEnergyControllerPrivateWinRT::handleCharacteristicChanged(
1130 quint16 charHandle, const QByteArray &data)
1131{
1132 qCDebug(QT_BT_WINRT) << __FUNCTION__ << charHandle << data;
1133 qCDebug(QT_BT_WINRT_SERVICE_THREAD) << __FUNCTION__ << "Changing service pointer from thread"
1134 << QThread::currentThread();
1135 QSharedPointer<QLowEnergyServicePrivate> service =
1136 serviceForHandle(charHandle);
1137 if (service.isNull())
1138 return;
1139
1140 qCDebug(QT_BT_WINRT) << "Characteristic change notification" << service->uuid
1141 << charHandle << data.toHex();
1142
1143 QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle);
1144 if (!characteristic.isValid()) {
1145 qCWarning(QT_BT_WINRT) << "characteristicChanged: Cannot find characteristic";
1146 return;
1147 }
1148
1149 // only update cache when property is readable. Otherwise it remains
1150 // empty.
1151 if (characteristic.properties() & QLowEnergyCharacteristic::Read)
1152 updateValueOfCharacteristic(characteristic.attributeHandle(),
1153 data, false);
1154 emit service->characteristicChanged(characteristic, data);
1155}
1156
1157QT_END_NAMESPACE
1158
1159#include "qlowenergycontroller_winrt.moc"
1160

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