diff options
Diffstat (limited to 'src/bluetooth/qlowenergycontroller_winrt_new.cpp')
-rw-r--r-- | src/bluetooth/qlowenergycontroller_winrt_new.cpp | 1588 |
1 files changed, 1588 insertions, 0 deletions
diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp new file mode 100644 index 00000000..45a80252 --- /dev/null +++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp @@ -0,0 +1,1588 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlowenergycontroller_winrt_new_p.h" +#include "qlowenergycontroller_winrt_p.h" +#include "qbluetoothutils_winrt_p.h" + +#include <QtBluetooth/QLowEnergyCharacteristicData> +#include <QtBluetooth/QLowEnergyDescriptorData> +#include <QtBluetooth/private/qbluetoothutils_winrt_p.h> + +#ifdef CLASSIC_APP_BUILD +#define Q_OS_WINRT +#endif +#include <QtCore/qfunctions_winrt.h> +#include <QtCore/QtEndian> +#include <QtCore/QLoggingCategory> +#include <private/qeventdispatcher_winrt_p.h> + +#include <functional> +#include <robuffer.h> +#include <windows.devices.enumeration.h> +#include <windows.devices.bluetooth.h> +#include <windows.foundation.collections.h> +#include <windows.foundation.metadata.h> +#include <windows.storage.streams.h> + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Foundation::Metadata; +using namespace ABI::Windows::Devices; +using namespace ABI::Windows::Devices::Bluetooth; +using namespace ABI::Windows::Devices::Bluetooth::GenericAttributeProfile; +using namespace ABI::Windows::Devices::Enumeration; +using namespace ABI::Windows::Storage::Streams; + +QT_BEGIN_NAMESPACE + +typedef ITypedEventHandler<BluetoothLEDevice *, IInspectable *> StatusHandler; +typedef ITypedEventHandler<GattCharacteristic *, GattValueChangedEventArgs *> ValueChangedHandler; +typedef GattReadClientCharacteristicConfigurationDescriptorResult ClientCharConfigDescriptorResult; +typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharConfigDescriptorResult; + +#define EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, ret) \ + if (FAILED(hr)) { \ + emitErrorAndQuitThread(hr); \ + ret; \ + } + +#define WARN_AND_CONTINUE_IF_FAILED(hr, msg) \ + if (FAILED(hr)) { \ + qCWarning(QT_BT_WINRT) << msg; \ + continue; \ + } + +#define CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, msg, ret) \ + if (FAILED(hr)) { \ + qCWarning(QT_BT_WINRT) << msg; \ + unregisterFromStatusChanges(); \ + setError(QLowEnergyController::ConnectionError); \ + setState(QLowEnergyController::UnconnectedState); \ + ret; \ + } + +#define CHECK_HR_AND_SET_SERVICE_ERROR(hr, msg, service, error, ret) \ + if (FAILED(hr)) { \ + qCDebug(QT_BT_WINRT) << msg; \ + service->setError(error); \ + ret; \ + } + +Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT) + +QLowEnergyControllerPrivate *createWinRTLowEnergyController() +{ + if (supportsNewLEApi()) { + qCDebug(QT_BT_WINRT) << "Using new low energy controller"; + return new QLowEnergyControllerPrivateWinRTNew(); + } + + qCDebug(QT_BT_WINRT) << "Using pre 15063 low energy controller"; + return new QLowEnergyControllerPrivateWinRT(); +} + +static QByteArray byteArrayFromGattResult(const ComPtr<IGattReadResult> &gattResult, + bool isWCharString = false) +{ + ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer; + HRESULT hr; + hr = gattResult->get_Value(&buffer); + if (FAILED(hr) || !buffer) { + qCWarning(QT_BT_WINRT) << "Could not obtain buffer from GattReadResult"; + return QByteArray(); + } + return byteArrayFromBuffer(buffer, isWCharString); +} + +class QWinRTLowEnergyServiceHandlerNew : public QObject +{ + Q_OBJECT +public: + QWinRTLowEnergyServiceHandlerNew(const QBluetoothUuid &service, + const ComPtr<IGattDeviceService3> &deviceService) + : mService(service) + , mDeviceService(deviceService) + { + qCDebug(QT_BT_WINRT) << __FUNCTION__; + } + + ~QWinRTLowEnergyServiceHandlerNew() + { + } + +public slots: + void obtainCharList() + { + mIndicateChars.clear(); + qCDebug(QT_BT_WINRT) << __FUNCTION__; + ComPtr<IAsyncOperation<GattCharacteristicsResult *>> characteristicsOp; + ComPtr<IGattCharacteristicsResult> characteristicsResult; + HRESULT hr = mDeviceService->GetCharacteristicsAsync(&characteristicsOp); + EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return); + hr = QWinRTFunctions::await(characteristicsOp, characteristicsResult.GetAddressOf(), + QWinRTFunctions::ProcessMainThreadEvents, 5000); + EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return); + GattCommunicationStatus status; + hr = characteristicsResult->get_Status(&status); + EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return); + if (status != GattCommunicationStatus_Success) { + emitErrorAndQuitThread(QLatin1String("Could not obtain char list")); + return; + } + ComPtr<IVectorView<GattCharacteristic *>> characteristics; + hr = characteristicsResult->get_Characteristics(&characteristics); + EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return); + + uint characteristicsCount; + hr = characteristics->get_Size(&characteristicsCount); + EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return); + + mCharacteristicsCountToBeDiscovered = characteristicsCount; + for (uint i = 0; i < characteristicsCount; ++i) { + ComPtr<IGattCharacteristic> characteristic; + hr = characteristics->GetAt(i, &characteristic); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain characteristic at" << i; + --mCharacteristicsCountToBeDiscovered; + continue; + } + + ComPtr<IGattCharacteristic3> characteristic3; + hr = characteristic.As(&characteristic3); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not cast characteristic"; + --mCharacteristicsCountToBeDiscovered; + continue; + } + + // For some strange reason, Windows doesn't discover descriptors of characteristics (if not paired). + // Qt API assumes that all characteristics and their descriptors are discovered in one go. + // So we start 'GetDescriptorsAsync' for each discovered characteristic and finish only + // when GetDescriptorsAsync for all characteristics return. + ComPtr<IAsyncOperation<GattDescriptorsResult*>> descAsyncResult; + hr = characteristic3->GetDescriptorsAsync(&descAsyncResult); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain list of descriptors"; + --mCharacteristicsCountToBeDiscovered; + continue; + } + hr = descAsyncResult->put_Completed( + Callback<IAsyncOperationCompletedHandler<GattDescriptorsResult*>>( + [this, characteristic] + (IAsyncOperation<GattDescriptorsResult *> *op, + AsyncStatus status) { + if (status != AsyncStatus::Completed) { + qCWarning(QT_BT_WINRT) << "Descriptor operation unsuccessful"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + quint16 handle; + + HRESULT hr = characteristic->get_AttributeHandle(&handle); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain characteristic's attribute handle"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + QLowEnergyServicePrivate::CharData charData; + charData.valueHandle = handle + 1; + if (mStartHandle == 0 || mStartHandle > handle) + mStartHandle = handle; + if (mEndHandle == 0 || mEndHandle < handle) + mEndHandle = handle; + GUID guuid; + hr = characteristic->get_Uuid(&guuid); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain characteristic's Uuid"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + charData.uuid = QBluetoothUuid(guuid); + GattCharacteristicProperties properties; + hr = characteristic->get_CharacteristicProperties(&properties); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain characteristic's properties"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + charData.properties = QLowEnergyCharacteristic::PropertyTypes(properties & 0xff); + if (charData.properties & QLowEnergyCharacteristic::Read) { + ComPtr<IAsyncOperation<GattReadResult *>> readOp; + hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, + &readOp); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not read characteristic"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + ComPtr<IGattReadResult> readResult; + hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain characteristic read result"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + if (!readResult) + qCWarning(QT_BT_WINRT) << "Characteristic read result is null"; + else + charData.value = byteArrayFromGattResult(readResult); + } + mCharacteristicList.insert(handle, charData); + + ComPtr<IVectorView<GattDescriptor *>> descriptors; + + ComPtr<IGattDescriptorsResult> result; + hr = op->GetResults(&result); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain descriptor read result"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + GattCommunicationStatus commStatus; + hr = result->get_Status(&commStatus); + if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) { + qCWarning(QT_BT_WINRT) << "Descriptor operation failed"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + + hr = result->get_Descriptors(&descriptors); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain list of descriptors"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + + uint descriptorCount; + hr = descriptors->get_Size(&descriptorCount); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not obtain list of descriptors' size"; + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + } + for (uint j = 0; j < descriptorCount; ++j) { + QLowEnergyServicePrivate::DescData descData; + ComPtr<IGattDescriptor> descriptor; + hr = descriptors->GetAt(j, &descriptor); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor") + quint16 descHandle; + hr = descriptor->get_AttributeHandle(&descHandle); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor's attribute handle") + GUID descriptorUuid; + hr = descriptor->get_Uuid(&descriptorUuid); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor's Uuid") + descData.uuid = QBluetoothUuid(descriptorUuid); + charData.descriptorList.insert(descHandle, descData); + if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) { + ComPtr<IAsyncOperation<ClientCharConfigDescriptorResult *>> readOp; + hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not read descriptor value") + ComPtr<IClientCharConfigDescriptorResult> readResult; + hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not await descriptor read result") + GattClientCharacteristicConfigurationDescriptorValue value; + hr = readResult->get_ClientCharacteristicConfigurationDescriptor(&value); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not get descriptor value from result") + quint16 result = 0; + bool correct = false; + if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) { + result |= GattClientCharacteristicConfigurationDescriptorValue_Indicate; + correct = true; + } + if (value & GattClientCharacteristicConfigurationDescriptorValue_Notify) { + result |= GattClientCharacteristicConfigurationDescriptorValue_Notify; + correct = true; + } + if (value == GattClientCharacteristicConfigurationDescriptorValue_None) { + correct = true; + } + if (!correct) + continue; + + descData.value = QByteArray(2, Qt::Uninitialized); + qToLittleEndian(result, descData.value.data()); + mIndicateChars << charData.uuid; + } else { + ComPtr<IAsyncOperation<GattReadResult *>> readOp; + hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, + &readOp); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not read descriptor value") + ComPtr<IGattReadResult> readResult; + hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could await descriptor read result") + if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription) + descData.value = byteArrayFromGattResult(readResult, true); + else + descData.value = byteArrayFromGattResult(readResult); + } + charData.descriptorList.insert(descHandle, descData); + } + + mCharacteristicList.insert(handle, charData); + --mCharacteristicsCountToBeDiscovered; + checkAllCharacteristicsDiscovered(); + return S_OK; + }).Get()); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << "Could not register descriptor callback"; + --mCharacteristicsCountToBeDiscovered; + continue; + } + } + checkAllCharacteristicsDiscovered(); + } + +private: + bool checkAllCharacteristicsDiscovered(); + void emitErrorAndQuitThread(HRESULT hr); + void emitErrorAndQuitThread(const QString &error); + +public: + QBluetoothUuid mService; + ComPtr<IGattDeviceService3> mDeviceService; + QHash<QLowEnergyHandle, QLowEnergyServicePrivate::CharData> mCharacteristicList; + uint mCharacteristicsCountToBeDiscovered; + quint16 mStartHandle = 0; + quint16 mEndHandle = 0; + QVector<QBluetoothUuid> mIndicateChars; + +signals: + void charListObtained(const QBluetoothUuid &service, QHash<QLowEnergyHandle, + QLowEnergyServicePrivate::CharData> charList, + QVector<QBluetoothUuid> indicateChars, + QLowEnergyHandle startHandle, QLowEnergyHandle endHandle); + void errorOccured(const QString &error); +}; + +bool QWinRTLowEnergyServiceHandlerNew::checkAllCharacteristicsDiscovered() +{ + if (mCharacteristicsCountToBeDiscovered == 0) { + emit charListObtained(mService, mCharacteristicList, mIndicateChars, + mStartHandle, mEndHandle); + QThread::currentThread()->quit(); + return true; + } + + return false; +} + +void QWinRTLowEnergyServiceHandlerNew::emitErrorAndQuitThread(HRESULT hr) +{ + emitErrorAndQuitThread(qt_error_string(hr)); +} + +void QWinRTLowEnergyServiceHandlerNew::emitErrorAndQuitThread(const QString &error) +{ + emit errorOccured(error); + QThread::currentThread()->quit(); +} + +QLowEnergyControllerPrivateWinRTNew::QLowEnergyControllerPrivateWinRTNew() + : QLowEnergyControllerPrivate() +{ + registerQLowEnergyControllerMetaType(); +} + +QLowEnergyControllerPrivateWinRTNew::~QLowEnergyControllerPrivateWinRTNew() +{ + unregisterFromStatusChanges(); + unregisterFromValueChanges(); +} + +void QLowEnergyControllerPrivateWinRTNew::init() +{ +} + +void QLowEnergyControllerPrivateWinRTNew::connectToDevice() +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__; + Q_Q(QLowEnergyController); + if (remoteDevice.isNull()) { + qWarning() << "Invalid/null remote device address"; + setError(QLowEnergyController::UnknownRemoteDeviceError); + return; + } + + setState(QLowEnergyController::ConnectingState); + + ComPtr<IBluetoothLEDeviceStatics> deviceStatics; + HRESULT hr = GetActivationFactory( + HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice).Get(), + &deviceStatics); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain device factory", return) + ComPtr<IAsyncOperation<BluetoothLEDevice *>> deviceFromIdOperation; + hr = deviceStatics->FromBluetoothAddressAsync(remoteDevice.toUInt64(), &deviceFromIdOperation); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not find LE device from address", return) + hr = QWinRTFunctions::await(deviceFromIdOperation, mDevice.GetAddressOf(), + QWinRTFunctions::ProcessMainThreadEvents, 5000); + if (FAILED(hr) || !mDevice) { + qCWarning(QT_BT_WINRT) << "Could not find LE device"; + setError(QLowEnergyController::InvalidBluetoothAdapterError); + setState(QLowEnergyController::UnconnectedState); + return; + } + BluetoothConnectionStatus status; + hr = mDevice->get_ConnectionStatus(&status); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain device's connection status", return) + hr = QEventDispatcherWinRT::runOnXamlThread([this, q]() { + HRESULT hr; + hr = mDevice->add_ConnectionStatusChanged( + Callback<StatusHandler>([this, q](IBluetoothLEDevice *dev, IInspectable *) { + BluetoothConnectionStatus status; + HRESULT hr; + hr = dev->get_ConnectionStatus(&status); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain connection status", return S_OK) + if (state == QLowEnergyController::ConnectingState + && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) { + setState(QLowEnergyController::ConnectedState); + emit q->connected(); + } else if (state != QLowEnergyController::UnconnectedState + && status == BluetoothConnectionStatus::BluetoothConnectionStatus_Disconnected) { + invalidateServices(); + unregisterFromValueChanges(); + unregisterFromStatusChanges(); + mDevice = nullptr; + setError(QLowEnergyController::RemoteHostClosedError); + setState(QLowEnergyController::UnconnectedState); + emit q->disconnected(); + } + return S_OK; + }).Get(), &mStatusChangedToken); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not register connection status callback", return S_OK) + return S_OK; + }); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not add status callback on Xaml thread", return) + + if (status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) { + setState(QLowEnergyController::ConnectedState); + emit q->connected(); + return; + } + + ComPtr<IBluetoothLEDevice3> device3; + hr = mDevice.As(&device3); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast device", return) + ComPtr<IAsyncOperation<GattDeviceServicesResult *>> deviceServicesOp; + hr = device3->GetGattServicesAsync(&deviceServicesOp); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain services", return) + ComPtr<IGattDeviceServicesResult> deviceServicesResult; + hr = QWinRTFunctions::await(deviceServicesOp, deviceServicesResult.GetAddressOf(), + QWinRTFunctions::ProcessMainThreadEvents, 5000); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await services operation", return) + + GattCommunicationStatus commStatus; + hr = deviceServicesResult->get_Status(&commStatus); + if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) { + qCWarning(QT_BT_WINRT()) << "Service operation failed"; + setError(QLowEnergyController::ConnectionError); + setState(QLowEnergyController::UnconnectedState); + unregisterFromStatusChanges(); + return; + } + + ComPtr<IVectorView <GattDeviceService *>> deviceServices; + hr = deviceServicesResult->get_Services(&deviceServices); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain list of services", return) + uint serviceCount; + hr = deviceServices->get_Size(&serviceCount); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service count", return) + + // Windows automatically connects to the device as soon as a service value is read/written. + // Thus we read one value in order to establish the connection. + for (uint i = 0; i < serviceCount; ++i) { + ComPtr<IGattDeviceService> service; + hr = deviceServices->GetAt(i, &service); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service"); + ComPtr<IGattDeviceService3> service3; + hr = service.As(&service3); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not cast service"); + ComPtr<IAsyncOperation<GattCharacteristicsResult *>> characteristicsOp; + hr = service3->GetCharacteristicsAsync(&characteristicsOp); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic"); + ComPtr<IGattCharacteristicsResult> characteristicsResult; + hr = QWinRTFunctions::await(characteristicsOp, characteristicsResult.GetAddressOf(), + QWinRTFunctions::ProcessMainThreadEvents, 5000); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not await characteristic operation"); + GattCommunicationStatus commStatus; + hr = characteristicsResult->get_Status(&commStatus); + if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) { + qCWarning(QT_BT_WINRT) << "Characteristic operation failed"; + continue; + } + ComPtr<IVectorView<GattCharacteristic *>> characteristics; + hr = characteristicsResult->get_Characteristics(&characteristics); + if (hr == E_ACCESSDENIED) { + // Everything will work as expected up until this point if the manifest capabilties + // for bluetooth LE are not set. + qCWarning(QT_BT_WINRT) << "Could not obtain characteristic list. Please check your " + "manifest capabilities"; + setState(QLowEnergyController::UnconnectedState); + setError(QLowEnergyController::ConnectionError); + unregisterFromStatusChanges(); + return; + } + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic list"); + uint characteristicsCount; + hr = characteristics->get_Size(&characteristicsCount); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic list's size"); + for (uint j = 0; j < characteristicsCount; ++j) { + ComPtr<IGattCharacteristic> characteristic; + hr = characteristics->GetAt(j, &characteristic); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic"); + ComPtr<IAsyncOperation<GattReadResult *>> op; + GattCharacteristicProperties props; + hr = characteristic->get_CharacteristicProperties(&props); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic's properties"); + if (!(props & GattCharacteristicProperties_Read)) + continue; + hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode::BluetoothCacheMode_Uncached, &op); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not read characteristic value"); + ComPtr<IGattReadResult> result; + hr = QWinRTFunctions::await(op, result.GetAddressOf()); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could await characteristic read"); + ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer; + hr = result->get_Value(&buffer); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic value"); + if (!buffer) { + qCDebug(QT_BT_WINRT) << "Problem reading value"; + continue; + } + return; + } + } + + qCWarning(QT_BT_WINRT) << "Could not obtain characteristic read result that triggers" + "device connection. Is the device reachable?"; + unregisterFromStatusChanges(); + setError(QLowEnergyController::ConnectionError); + setState(QLowEnergyController::UnconnectedState); +} + +void QLowEnergyControllerPrivateWinRTNew::disconnectFromDevice() +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__; + Q_Q(QLowEnergyController); + setState(QLowEnergyController::ClosingState); + unregisterFromValueChanges(); + unregisterFromStatusChanges(); + mDevice = nullptr; + setState(QLowEnergyController::UnconnectedState); + emit q->disconnected(); +} + +ComPtr<IGattDeviceService> QLowEnergyControllerPrivateWinRTNew::getNativeService( + const QBluetoothUuid &serviceUuid) +{ + ComPtr<IGattDeviceService> deviceService; + HRESULT hr; + hr = mDevice->GetGattService(serviceUuid, &deviceService); + if (FAILED(hr)) + qCDebug(QT_BT_WINRT) << "Could not obtain native service for Uuid" << serviceUuid; + return deviceService; +} + +ComPtr<IGattCharacteristic> QLowEnergyControllerPrivateWinRTNew::getNativeCharacteristic( + const QBluetoothUuid &serviceUuid, const QBluetoothUuid &charUuid) +{ + ComPtr<IGattDeviceService> service = getNativeService(serviceUuid); + if (!service) + return nullptr; + + ComPtr<IGattDeviceService3> service3; + HRESULT hr = service.As(&service3); + RETURN_IF_FAILED("Could not cast service", return nullptr); + + ComPtr<IAsyncOperation<GattCharacteristicsResult *>> op; + ComPtr<IGattCharacteristicsResult> result; + hr = service3->GetCharacteristicsForUuidAsync(charUuid, &op); + RETURN_IF_FAILED("Could not obtain native characteristics for service", return nullptr); + hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000); + RETURN_IF_FAILED("Could not await completion of characteristic operation", return nullptr); + GattCommunicationStatus status; + hr = result->get_Status(&status); + if (FAILED(hr) || status != GattCommunicationStatus_Success) { + qErrnoWarning(hr, "Native characteristic operation failed."); + return nullptr; + } + ComPtr<IVectorView<GattCharacteristic *>> characteristics; + hr = result->get_Characteristics(&characteristics); + RETURN_IF_FAILED("Could not obtain characteristic list.", return nullptr); + uint size; + hr = characteristics->get_Size(&size); + RETURN_IF_FAILED("Could not obtain characteristic list's size.", return nullptr); + if (size != 1) + qErrnoWarning("More than 1 characteristic found."); + ComPtr<IGattCharacteristic> characteristic; + hr = characteristics->GetAt(0, &characteristic); + RETURN_IF_FAILED("Could not obtain first characteristic for service", return nullptr); + return characteristic; +} + +void QLowEnergyControllerPrivateWinRTNew::registerForValueChanges(const QBluetoothUuid &serviceUuid, + const QBluetoothUuid &charUuid) +{ + qCDebug(QT_BT_WINRT) << "Registering characteristic" << charUuid << "in service" + << serviceUuid << "for value changes"; + for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens)) { + GUID guuid; + HRESULT hr; + hr = entry.characteristic->get_Uuid(&guuid); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic's Uuid") + if (QBluetoothUuid(guuid) == charUuid) + return; + } + ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(serviceUuid, charUuid); + if (!characteristic) { + qCDebug(QT_BT_WINRT).nospace() << "Could not obtain native characteristic " << charUuid + << " from service " << serviceUuid << ". Qt will not be able to signal" + << " changes for this characteristic."; + return; + } + + EventRegistrationToken token; + HRESULT hr; + hr = characteristic->add_ValueChanged( + Callback<ValueChangedHandler>( + [this](IGattCharacteristic *characteristic, IGattValueChangedEventArgs *args) { + HRESULT hr; + quint16 handle; + hr = characteristic->get_AttributeHandle(&handle); + RETURN_IF_FAILED("Could not obtain characteristic's handle", return S_OK) + ComPtr<IBuffer> buffer; + hr = args->get_CharacteristicValue(&buffer); + RETURN_IF_FAILED("Could not obtain characteristic's value", return S_OK) + characteristicChanged(handle, byteArrayFromBuffer(buffer)); + return S_OK; + }).Get(), &token); + RETURN_IF_FAILED("Could not register characteristic for value changes", return) + mValueChangedTokens.append(ValueChangedEntry(characteristic, token)); + qCDebug(QT_BT_WINRT) << "Characteristic" << charUuid << "in service" + << serviceUuid << "registered for value changes"; +} + +void QLowEnergyControllerPrivateWinRTNew::unregisterFromValueChanges() +{ + qCDebug(QT_BT_WINRT) << "Unregistering " << mValueChangedTokens.count() << " value change tokens"; + HRESULT hr; + for (const ValueChangedEntry &entry : qAsConst(mValueChangedTokens)) { + if (!entry.characteristic) { + qCWarning(QT_BT_WINRT) << "Unregistering from value changes for characteristic failed." + << "Characteristic has been deleted"; + continue; + } + hr = entry.characteristic->remove_ValueChanged(entry.token); + if (FAILED(hr)) + qCWarning(QT_BT_WINRT) << "Unregistering from value changes for characteristic failed."; + } + mValueChangedTokens.clear(); +} + +void QLowEnergyControllerPrivateWinRTNew::unregisterFromStatusChanges() +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__; + if (mDevice && mStatusChangedToken.value) { + mDevice->remove_ConnectionStatusChanged(mStatusChangedToken); + mStatusChangedToken.value = 0; + } +} + +void QLowEnergyControllerPrivateWinRTNew::obtainIncludedServices( + QSharedPointer<QLowEnergyServicePrivate> servicePointer, + ComPtr<IGattDeviceService> service) +{ + Q_Q(QLowEnergyController); + ComPtr<IGattDeviceService3> service3; + HRESULT hr = service.As(&service3); + RETURN_IF_FAILED("Could not cast service", return); + ComPtr<IAsyncOperation<GattDeviceServicesResult *>> op; + hr = service3->GetIncludedServicesAsync(&op); + // Some devices return ERROR_ACCESS_DISABLED_BY_POLICY + RETURN_IF_FAILED("Could not obtain included services", return); + ComPtr<IGattDeviceServicesResult> result; + hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000); + RETURN_IF_FAILED("Could not await service operation", return); + GattCommunicationStatus status; + hr = result->get_Status(&status); + if (FAILED(hr) || status != GattCommunicationStatus_Success) { + qErrnoWarning("Could not obtain list of included services"); + return; + } + ComPtr<IVectorView<GattDeviceService *>> includedServices; + hr = result->get_Services(&includedServices); + RETURN_IF_FAILED("Could not obtain service list", return); + + uint count; + hr = includedServices->get_Size(&count); + RETURN_IF_FAILED("Could not obtain service list's size", return); + for (uint i = 0; i < count; ++i) { + ComPtr<IGattDeviceService> includedService; + hr = includedServices->GetAt(i, &includedService); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service from list"); + GUID guuid; + hr = includedService->get_Uuid(&guuid); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain included service's Uuid"); + const QBluetoothUuid includedUuid(guuid); + QSharedPointer<QLowEnergyServicePrivate> includedPointer; + if (serviceList.contains(includedUuid)) { + includedPointer = serviceList.value(includedUuid); + } else { + QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate(); + priv->uuid = includedUuid; + priv->setController(this); + + includedPointer = QSharedPointer<QLowEnergyServicePrivate>(priv); + serviceList.insert(includedUuid, includedPointer); + } + includedPointer->type |= QLowEnergyService::IncludedService; + servicePointer->includedServices.append(includedUuid); + + obtainIncludedServices(includedPointer, includedService); + + emit q->serviceDiscovered(includedUuid); + } +} + +void QLowEnergyControllerPrivateWinRTNew::discoverServices() +{ + Q_Q(QLowEnergyController); + + qCDebug(QT_BT_WINRT) << "Service discovery initiated"; + + ComPtr<IBluetoothLEDevice3> device3; + HRESULT hr = mDevice.As(&device3); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast device", return); + ComPtr<IAsyncOperation<GenericAttributeProfile::GattDeviceServicesResult *>> asyncResult; + hr = device3->GetGattServicesAsync(&asyncResult); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain services", return); + hr = QEventDispatcherWinRT::runOnXamlThread( [asyncResult, q, this] () { + HRESULT hr = asyncResult->put_Completed( + Callback<IAsyncOperationCompletedHandler<GenericAttributeProfile::GattDeviceServicesResult *>>( + [this, q](IAsyncOperation<GenericAttributeProfile::GattDeviceServicesResult *> *op, + AsyncStatus status) { + if (status != AsyncStatus::Completed) { + qCDebug(QT_BT_WINRT) << "Could not obtain services"; + return S_OK; + } + ComPtr<IGattDeviceServicesResult> result; + ComPtr<IVectorView<GattDeviceService *>> deviceServices; + HRESULT hr = op->GetResults(&result); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service discovery result", + return S_OK); + GattCommunicationStatus commStatus; + hr = result->get_Status(&commStatus); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service discovery status", + return S_OK); + if (commStatus != GattCommunicationStatus_Success) + return S_OK; + + hr = result->get_Services(&deviceServices); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service list", + return S_OK); + + uint serviceCount; + hr = deviceServices->get_Size(&serviceCount); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service list size", + return S_OK); + for (uint i = 0; i < serviceCount; ++i) { + ComPtr<IGattDeviceService> deviceService; + hr = deviceServices->GetAt(i, &deviceService); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service"); + GUID guuid; + hr = deviceService->get_Uuid(&guuid); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service's Uuid"); + const QBluetoothUuid service(guuid); + + QSharedPointer<QLowEnergyServicePrivate> pointer; + if (serviceList.contains(service)) { + pointer = serviceList.value(service); + } else { + QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate(); + priv->uuid = service; + priv->setController(this); + + pointer = QSharedPointer<QLowEnergyServicePrivate>(priv); + serviceList.insert(service, pointer); + } + pointer->type |= QLowEnergyService::PrimaryService; + + obtainIncludedServices(pointer, deviceService); + + emit q->serviceDiscovered(service); + } + + setState(QLowEnergyController::DiscoveredState); + emit q->discoveryFinished(); + + return S_OK; + }).Get()); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not register service discovery callback", + return S_OK) + return hr; + }); + CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not run registration in Xaml thread", + return) +} + +void QLowEnergyControllerPrivateWinRTNew::discoverServiceDetails(const QBluetoothUuid &service) +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__ << service; + if (!serviceList.contains(service)) { + qCWarning(QT_BT_WINRT) << "Discovery done of unknown service:" + << service.toString(); + return; + } + + ComPtr<IGattDeviceService> deviceService = getNativeService(service); + if (!deviceService) { + qCDebug(QT_BT_WINRT) << "Could not obtain native service for uuid " << service; + return; + } + + //update service data + QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service); + + pointer->setState(QLowEnergyService::DiscoveringServices); + ComPtr<IGattDeviceService3> deviceService3; + HRESULT hr = deviceService.As(&deviceService3); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast service", + pointer, QLowEnergyService::UnknownError, return) + ComPtr<IAsyncOperation<GattDeviceServicesResult *>> op; + hr = deviceService3->GetIncludedServicesAsync(&op); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain included service list", + pointer, QLowEnergyService::UnknownError, return) + ComPtr<IGattDeviceServicesResult> result; + hr = QWinRTFunctions::await(op, result.GetAddressOf()); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await service operation", + pointer, QLowEnergyService::UnknownError, return) + GattCommunicationStatus status; + hr = result->get_Status(&status); + if (FAILED(hr) || status != GattCommunicationStatus_Success) { + qCDebug(QT_BT_WINRT) << "Obtaining list of included services failed"; + pointer->setError(QLowEnergyService::UnknownError); + return; + } + ComPtr<IVectorView<GattDeviceService *>> deviceServices; + hr = result->get_Services(&deviceServices); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain service list from result", + pointer, QLowEnergyService::UnknownError, return) + uint serviceCount; + hr = deviceServices->get_Size(&serviceCount); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain included service list's size", + pointer, QLowEnergyService::UnknownError, return) + for (uint i = 0; i < serviceCount; ++i) { + ComPtr<IGattDeviceService> includedService; + hr = deviceServices->GetAt(i, &includedService); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service from list") + GUID guuid; + hr = includedService->get_Uuid(&guuid); + WARN_AND_CONTINUE_IF_FAILED(hr, "Could not obtain service Uuid") + + const QBluetoothUuid service(guuid); + if (service.isNull()) { + qCDebug(QT_BT_WINRT) << "Could not find service"; + continue; + } + + pointer->includedServices.append(service); + + // update the type of the included service + QSharedPointer<QLowEnergyServicePrivate> otherService = serviceList.value(service); + if (!otherService.isNull()) + otherService->type |= QLowEnergyService::IncludedService; + } + + QWinRTLowEnergyServiceHandlerNew *worker + = new QWinRTLowEnergyServiceHandlerNew(service, deviceService3); + QThread *thread = new QThread; + worker->moveToThread(thread); + connect(thread, &QThread::started, worker, &QWinRTLowEnergyServiceHandlerNew::obtainCharList); + connect(thread, &QThread::finished, thread, &QObject::deleteLater); + connect(thread, &QThread::finished, worker, &QObject::deleteLater); + connect(worker, &QWinRTLowEnergyServiceHandlerNew::errorOccured, + this, &QLowEnergyControllerPrivateWinRTNew::handleServiceHandlerError); + connect(worker, &QWinRTLowEnergyServiceHandlerNew::charListObtained, + [this, thread](const QBluetoothUuid &service, QHash<QLowEnergyHandle, + QLowEnergyServicePrivate::CharData> charList, QVector<QBluetoothUuid> indicateChars, + QLowEnergyHandle startHandle, QLowEnergyHandle endHandle) { + if (!serviceList.contains(service)) { + qCWarning(QT_BT_WINRT) << "Discovery done of unknown service:" + << service.toString(); + return; + } + + QSharedPointer<QLowEnergyServicePrivate> pointer = serviceList.value(service); + pointer->startHandle = startHandle; + pointer->endHandle = endHandle; + pointer->characteristicList = charList; + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([indicateChars, service, this]() { + for (const QBluetoothUuid &indicateChar : qAsConst(indicateChars)) + registerForValueChanges(service, indicateChar); + return S_OK; + }); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register for value changes in Xaml thread", + pointer, QLowEnergyService::UnknownError, return) + + pointer->setState(QLowEnergyService::ServiceDiscovered); + thread->exit(0); + }); + thread->start(); +} + +void QLowEnergyControllerPrivateWinRTNew::startAdvertising( + const QLowEnergyAdvertisingParameters &, + const QLowEnergyAdvertisingData &, + const QLowEnergyAdvertisingData &) +{ + setError(QLowEnergyController::AdvertisingError); + Q_UNIMPLEMENTED(); +} + +void QLowEnergyControllerPrivateWinRTNew::stopAdvertising() +{ + Q_UNIMPLEMENTED(); +} + +void QLowEnergyControllerPrivateWinRTNew::requestConnectionUpdate(const QLowEnergyConnectionParameters &) +{ + Q_UNIMPLEMENTED(); +} + +void QLowEnergyControllerPrivateWinRTNew::readCharacteristic( + const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle) +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle; + Q_ASSERT(!service.isNull()); + if (role == QLowEnergyController::PeripheralRole) { + service->setError(QLowEnergyService::CharacteristicReadError); + Q_UNIMPLEMENTED(); + return; + } + + if (!service->characteristicList.contains(charHandle)) { + qCDebug(QT_BT_WINRT) << charHandle << "could not be found in service" << service->uuid; + service->setError(QLowEnergyService::CharacteristicReadError); + return; + } + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([charHandle, service, this]() { + const QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle); + if (!(charData.properties & QLowEnergyCharacteristic::Read)) + qCDebug(QT_BT_WINRT) << "Read flag is not set for characteristic" << charData.uuid; + + ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, charData.uuid); + if (!characteristic) { + qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid + << "from service" << service->uuid; + service->setError(QLowEnergyService::CharacteristicReadError); + return S_OK; + } + ComPtr<IAsyncOperation<GattReadResult*>> readOp; + HRESULT hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not read characteristic", + service, QLowEnergyService::CharacteristicReadError, return S_OK) + auto readCompletedLambda = [charData, charHandle, service] + (IAsyncOperation<GattReadResult*> *op, AsyncStatus status) + { + if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { + qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "read operation failed."; + service->setError(QLowEnergyService::CharacteristicReadError); + return S_OK; + } + ComPtr<IGattReadResult> characteristicValue; + HRESULT hr; + hr = op->GetResults(&characteristicValue); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for characteristic", + service, QLowEnergyService::CharacteristicReadError, return S_OK) + + const QByteArray value = byteArrayFromGattResult(characteristicValue); + QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle); + charData.value = value; + service->characteristicList.insert(charHandle, charData); + emit service->characteristicRead(QLowEnergyCharacteristic(service, charHandle), value); + return S_OK; + }; + hr = readOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattReadResult *>>( + readCompletedLambda).Get()); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register characteristic read callback", + service, QLowEnergyService::CharacteristicReadError, return S_OK) + return S_OK; + }); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not run registration on Xaml thread", + service, QLowEnergyService::CharacteristicReadError, return) +} + +void QLowEnergyControllerPrivateWinRTNew::readDescriptor( + const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle, + const QLowEnergyHandle descHandle) +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << descHandle; + Q_ASSERT(!service.isNull()); + if (role == QLowEnergyController::PeripheralRole) { + service->setError(QLowEnergyService::DescriptorReadError); + Q_UNIMPLEMENTED(); + return; + } + + if (!service->characteristicList.contains(charHandle)) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "in characteristic" << charHandle + << "cannot be found in service" << service->uuid; + service->setError(QLowEnergyService::DescriptorReadError); + return; + } + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([charHandle, descHandle, service, this]() { + const QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle); + ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, charData.uuid); + if (!characteristic) { + qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid + << "from service" << service->uuid; + service->setError(QLowEnergyService::DescriptorReadError); + return S_OK; + } + + // Get native descriptor + if (!charData.descriptorList.contains(descHandle)) + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "cannot be found in characteristic" << charHandle; + const QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle); + const QBluetoothUuid descUuid = descData.uuid; + if (descUuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) { + ComPtr<IAsyncOperation<ClientCharConfigDescriptorResult *>> readOp; + HRESULT hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not read client characteristic configuration", + service, QLowEnergyService::DescriptorReadError, return S_OK) + auto readCompletedLambda = [charHandle, descHandle, service] + (IAsyncOperation<ClientCharConfigDescriptorResult *> *op, AsyncStatus status) + { + if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "read operation failed"; + service->setError(QLowEnergyService::DescriptorReadError); + return S_OK; + } + ComPtr<IClientCharConfigDescriptorResult> iValue; + HRESULT hr; + hr = op->GetResults(&iValue); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for descriptor", + service, QLowEnergyService::DescriptorReadError, return S_OK) + GattClientCharacteristicConfigurationDescriptorValue value; + hr = iValue->get_ClientCharacteristicConfigurationDescriptor(&value); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain value for descriptor", + service, QLowEnergyService::DescriptorReadError, return S_OK) + quint16 result = 0; + bool correct = false; + if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) { + result |= QLowEnergyCharacteristic::Indicate; + correct = true; + } + if (value & GattClientCharacteristicConfigurationDescriptorValue_Notify) { + result |= QLowEnergyCharacteristic::Notify; + correct = true; + } + if (value == GattClientCharacteristicConfigurationDescriptorValue_None) + correct = true; + if (!correct) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle + << "read operation failed. Obtained unexpected value."; + service->setError(QLowEnergyService::DescriptorReadError); + return S_OK; + } + QLowEnergyServicePrivate::DescData descData; + descData.uuid = QBluetoothUuid::ClientCharacteristicConfiguration; + descData.value = QByteArray(2, Qt::Uninitialized); + qToLittleEndian(result, descData.value.data()); + service->characteristicList[charHandle].descriptorList[descHandle] = descData; + emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle), + descData.value); + return S_OK; + }; + hr = readOp->put_Completed( + Callback<IAsyncOperationCompletedHandler<ClientCharConfigDescriptorResult *>>( + readCompletedLambda).Get()); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor read callback", + service, QLowEnergyService::DescriptorReadError, return S_OK) + return S_OK; + } else { + ComPtr<IGattCharacteristic3> characteristic3; + HRESULT hr = characteristic.As(&characteristic3); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast characteristic", + service, QLowEnergyService::DescriptorReadError, return S_OK) + ComPtr<IAsyncOperation<GattDescriptorsResult *>> op; + hr = characteristic3->GetDescriptorsForUuidAsync(descData.uuid, &op); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor for uuid", + service, QLowEnergyService::DescriptorReadError, return S_OK) + ComPtr<IGattDescriptorsResult> result; + hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await descritpor read result", + service, QLowEnergyService::DescriptorReadError, return S_OK) + + GattCommunicationStatus commStatus; + hr = result->get_Status(&commStatus); + if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) { + qErrnoWarning("Could not obtain list of descriptors"); + service->setError(QLowEnergyService::DescriptorReadError); + return S_OK; + } + + ComPtr<IVectorView<GattDescriptor *>> descriptors; + hr = result->get_Descriptors(&descriptors); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor list", + service, QLowEnergyService::DescriptorReadError, return S_OK) + uint size; + hr = descriptors->get_Size(&size); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await descritpor list's size", + service, QLowEnergyService::DescriptorReadError, return S_OK) + if (size == 0) { + qCWarning(QT_BT_WINRT) << "No descriptor with uuid" << descData.uuid << "was found."; + service->setError(QLowEnergyService::DescriptorReadError); + return S_OK; + } else if (size > 1) { + qCWarning(QT_BT_WINRT) << "There is more than 1 descriptor with uuid" << descData.uuid; + } + + ComPtr<IGattDescriptor> descriptor; + hr = descriptors->GetAt(0, &descriptor); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descritpor from list", + service, QLowEnergyService::DescriptorReadError, return S_OK) + ComPtr<IAsyncOperation<GattReadResult*>> readOp; + hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not read descriptor value", + service, QLowEnergyService::DescriptorReadError, return S_OK) + auto readCompletedLambda = [charHandle, descHandle, descUuid, service] + (IAsyncOperation<GattReadResult*> *op, AsyncStatus status) + { + if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "read operation failed"; + service->setError(QLowEnergyService::DescriptorReadError); + return S_OK; + } + ComPtr<IGattReadResult> descriptorValue; + HRESULT hr; + hr = op->GetResults(&descriptorValue); + if (FAILED(hr)) { + qCDebug(QT_BT_WINRT) << "Could not obtain result for descriptor" << descHandle; + service->setError(QLowEnergyService::DescriptorReadError); + return S_OK; + } + QLowEnergyServicePrivate::DescData descData; + descData.uuid = descUuid; + if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription) + descData.value = byteArrayFromGattResult(descriptorValue, true); + else + descData.value = byteArrayFromGattResult(descriptorValue); + service->characteristicList[charHandle].descriptorList[descHandle] = descData; + emit service->descriptorRead(QLowEnergyDescriptor(service, charHandle, descHandle), + descData.value); + return S_OK; + }; + hr = readOp->put_Completed(Callback<IAsyncOperationCompletedHandler<GattReadResult *>>( + readCompletedLambda).Get()); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor read callback", + service, QLowEnergyService::DescriptorReadError, return S_OK) + return S_OK; + } + }); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not run registration on Xaml thread", + service, QLowEnergyService::DescriptorReadError, return) +} + +void QLowEnergyControllerPrivateWinRTNew::writeCharacteristic( + const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle, + const QByteArray &newValue, + QLowEnergyService::WriteMode mode) +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << newValue << mode; + Q_ASSERT(!service.isNull()); + if (role == QLowEnergyController::PeripheralRole) { + service->setError(QLowEnergyService::CharacteristicWriteError); + Q_UNIMPLEMENTED(); + return; + } + if (!service->characteristicList.contains(charHandle)) { + qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "cannot be found in service" + << service->uuid; + service->setError(QLowEnergyService::CharacteristicWriteError); + return; + } + + QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle); + const bool writeWithResponse = mode == QLowEnergyService::WriteWithResponse; + if (!(charData.properties & (writeWithResponse ? QLowEnergyCharacteristic::Write + : QLowEnergyCharacteristic::WriteNoResponse))) + qCDebug(QT_BT_WINRT) << "Write flag is not set for characteristic" << charHandle; + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([charData, charHandle, this, service, newValue, + writeWithResponse]() { + ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, + charData.uuid); + if (!characteristic) { + qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid + << "from service" << service->uuid; + service->setError(QLowEnergyService::CharacteristicWriteError); + return S_OK; + } + ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory; + HRESULT hr = GetActivationFactory( + HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), + &bufferFactory); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain buffer factory", + service, QLowEnergyService::CharacteristicWriteError, return S_OK) + ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer; + const quint32 length = quint32(newValue.length()); + hr = bufferFactory->Create(length, &buffer); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not create buffer", + service, QLowEnergyService::CharacteristicWriteError, return S_OK) + hr = buffer->put_Length(length); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer length", + service, QLowEnergyService::CharacteristicWriteError, return S_OK) + ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess; + hr = buffer.As(&byteAccess); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast buffer", + service, QLowEnergyService::CharacteristicWriteError, return S_OK) + byte *bytes; + hr = byteAccess->Buffer(&bytes); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer", + service, QLowEnergyService::CharacteristicWriteError, return S_OK) + memcpy(bytes, newValue, length); + ComPtr<IAsyncOperation<GattCommunicationStatus>> writeOp; + GattWriteOption option = writeWithResponse ? GattWriteOption_WriteWithResponse + : GattWriteOption_WriteWithoutResponse; + hr = characteristic->WriteValueWithOptionAsync(buffer.Get(), option, &writeOp); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could write characteristic", + service, QLowEnergyService::CharacteristicWriteError, return S_OK) + auto writeCompletedLambda =[charData, charHandle, newValue, service, writeWithResponse, this] + (IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status) + { + if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { + qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "write operation failed"; + service->setError(QLowEnergyService::CharacteristicWriteError); + return S_OK; + } + GattCommunicationStatus result; + HRESULT hr; + hr = op->GetResults(&result); + if (hr == E_BLUETOOTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH) { + qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle + << "write operation was tried with invalid value length"; + service->setError(QLowEnergyService::CharacteristicWriteError); + return S_OK; + } + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain characteristic write result", + service, QLowEnergyService::CharacteristicWriteError, return S_OK) + if (result != GattCommunicationStatus_Success) { + qCDebug(QT_BT_WINRT) << "Characteristic" << charHandle << "write operation failed"; + service->setError(QLowEnergyService::CharacteristicWriteError); + return S_OK; + } + // only update cache when property is readable. Otherwise it remains + // empty. + if (charData.properties & QLowEnergyCharacteristic::Read) + updateValueOfCharacteristic(charHandle, newValue, false); + if (writeWithResponse) + emit service->characteristicWritten(QLowEnergyCharacteristic(service, charHandle), + newValue); + return S_OK; + }; + hr = writeOp->put_Completed( + Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus>>( + writeCompletedLambda).Get()); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register characteristic write callback", + service, QLowEnergyService::CharacteristicWriteError, return S_OK) + return S_OK; + }); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not run registration on Xaml thread", + service, QLowEnergyService::CharacteristicWriteError, return) +} + +void QLowEnergyControllerPrivateWinRTNew::writeDescriptor( + const QSharedPointer<QLowEnergyServicePrivate> service, + const QLowEnergyHandle charHandle, + const QLowEnergyHandle descHandle, + const QByteArray &newValue) +{ + qCDebug(QT_BT_WINRT) << __FUNCTION__ << service << charHandle << descHandle << newValue; + Q_ASSERT(!service.isNull()); + if (role == QLowEnergyController::PeripheralRole) { + service->setError(QLowEnergyService::DescriptorWriteError); + Q_UNIMPLEMENTED(); + return; + } + + if (!service->characteristicList.contains(charHandle)) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "in characteristic" << charHandle + << "could not be found in service" << service->uuid; + service->setError(QLowEnergyService::DescriptorWriteError); + return; + } + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([charHandle, descHandle, this, service, newValue]() { + const QLowEnergyServicePrivate::CharData charData = service->characteristicList.value(charHandle); + ComPtr<IGattCharacteristic> characteristic = getNativeCharacteristic(service->uuid, charData.uuid); + if (!characteristic) { + qCDebug(QT_BT_WINRT) << "Could not obtain native characteristic" << charData.uuid + << "from service" << service->uuid; + service->setError(QLowEnergyService::DescriptorWriteError); + return S_OK; + } + + // Get native descriptor + if (!charData.descriptorList.contains(descHandle)) + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "could not be found in Characteristic" + << charHandle; + + QLowEnergyServicePrivate::DescData descData = charData.descriptorList.value(descHandle); + if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) { + GattClientCharacteristicConfigurationDescriptorValue value; + quint16 intValue = qFromLittleEndian<quint16>(newValue); + if (intValue & GattClientCharacteristicConfigurationDescriptorValue_Indicate + && intValue & GattClientCharacteristicConfigurationDescriptorValue_Notify) { + qCWarning(QT_BT_WINRT) << "Setting both Indicate and Notify is not supported on WinRT"; + value = GattClientCharacteristicConfigurationDescriptorValue( + (GattClientCharacteristicConfigurationDescriptorValue_Indicate + | GattClientCharacteristicConfigurationDescriptorValue_Notify)); + } else if (intValue & GattClientCharacteristicConfigurationDescriptorValue_Indicate) { + value = GattClientCharacteristicConfigurationDescriptorValue_Indicate; + } else if (intValue & GattClientCharacteristicConfigurationDescriptorValue_Notify) { + value = GattClientCharacteristicConfigurationDescriptorValue_Notify; + } else if (intValue == 0) { + value = GattClientCharacteristicConfigurationDescriptorValue_None; + } else { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle + << "write operation failed: Invalid value"; + service->setError(QLowEnergyService::DescriptorWriteError); + return S_OK; + } + ComPtr<IAsyncOperation<enum GattCommunicationStatus>> writeOp; + HRESULT hr = characteristic->WriteClientCharacteristicConfigurationDescriptorAsync(value, &writeOp); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not write client characteristic configuration", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + auto writeCompletedLambda = [charHandle, descHandle, newValue, service, this] + (IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status) + { + if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed"; + service->setError(QLowEnergyService::DescriptorWriteError); + return S_OK; + } + GattCommunicationStatus result; + HRESULT hr; + hr = op->GetResults(&result); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for descriptor", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + if (result != GattCommunicationStatus_Success) { + qCWarning(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed"; + service->setError(QLowEnergyService::DescriptorWriteError); + return S_OK; + } + updateValueOfDescriptor(charHandle, descHandle, newValue, false); + emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle), + newValue); + return S_OK; + }; + hr = writeOp->put_Completed( + Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus >>( + writeCompletedLambda).Get()); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor write callback", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + } else { + ComPtr<IGattCharacteristic3> characteristic3; + HRESULT hr = characteristic.As(&characteristic3); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast characteristic", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + ComPtr<IAsyncOperation<GattDescriptorsResult *>> op; + hr = characteristic3->GetDescriptorsForUuidAsync(descData.uuid, &op); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor from Uuid", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + ComPtr<IGattDescriptorsResult> result; + hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not await descriptor operation", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + GattCommunicationStatus commStatus; + hr = result->get_Status(&commStatus); + if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) { + qCWarning(QT_BT_WINRT) << "Descriptor operation failed"; + service->setError(QLowEnergyService::DescriptorWriteError); + return S_OK; + } + ComPtr<IVectorView<GattDescriptor *>> descriptors; + hr = result->get_Descriptors(&descriptors); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain list of descriptors", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + uint size; + hr = descriptors->get_Size(&size); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain list of descriptors' size", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + if (size == 0) { + qCWarning(QT_BT_WINRT) << "No descriptor with uuid" << descData.uuid << "was found."; + return S_OK; + } else if (size > 1) { + qCWarning(QT_BT_WINRT) << "There is more than 1 descriptor with uuid" << descData.uuid; + } + ComPtr<IGattDescriptor> descriptor; + hr = descriptors->GetAt(0, &descriptor); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain descriptor", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory; + hr = GetActivationFactory( + HStringReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), + &bufferFactory); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain buffer factory", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer; + const quint32 length = quint32(newValue.length()); + hr = bufferFactory->Create(length, &buffer); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not create buffer", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + hr = buffer->put_Length(length); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer length", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteAccess; + hr = buffer.As(&byteAccess); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not cast buffer", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + byte *bytes; + hr = byteAccess->Buffer(&bytes); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not set buffer", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + memcpy(bytes, newValue, length); + ComPtr<IAsyncOperation<GattCommunicationStatus>> writeOp; + hr = descriptor->WriteValueAsync(buffer.Get(), &writeOp); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not write descriptor value", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + auto writeCompletedLambda = [charHandle, descHandle, newValue, service, this] + (IAsyncOperation<GattCommunicationStatus> *op, AsyncStatus status) + { + if (status == AsyncStatus::Canceled || status == AsyncStatus::Error) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed"; + service->setError(QLowEnergyService::DescriptorWriteError); + return S_OK; + } + GattCommunicationStatus result; + HRESULT hr; + hr = op->GetResults(&result); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not obtain result for descriptor", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + if (result != GattCommunicationStatus_Success) { + qCDebug(QT_BT_WINRT) << "Descriptor" << descHandle << "write operation failed"; + service->setError(QLowEnergyService::DescriptorWriteError); + return S_OK; + } + updateValueOfDescriptor(charHandle, descHandle, newValue, false); + emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle), + newValue); + return S_OK; + }; + hr = writeOp->put_Completed( + Callback<IAsyncOperationCompletedHandler<GattCommunicationStatus>>( + writeCompletedLambda).Get()); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not register descriptor write callback", + service, QLowEnergyService::DescriptorWriteError, return S_OK) + return S_OK; + } + return S_OK; + }); + CHECK_HR_AND_SET_SERVICE_ERROR(hr, "Could not run registration on Xaml thread", + service, QLowEnergyService::DescriptorWriteError, return) +} + + +void QLowEnergyControllerPrivateWinRTNew::addToGenericAttributeList(const QLowEnergyServiceData &, + QLowEnergyHandle) +{ + Q_UNIMPLEMENTED(); +} + +void QLowEnergyControllerPrivateWinRTNew::characteristicChanged( + quint16 charHandle, const QByteArray &data) +{ + QSharedPointer<QLowEnergyServicePrivate> service = + serviceForHandle(charHandle); + if (service.isNull()) + return; + + qCDebug(QT_BT_WINRT) << "Characteristic change notification" << service->uuid + << charHandle << data.toHex(); + + QLowEnergyCharacteristic characteristic = characteristicForHandle(charHandle); + if (!characteristic.isValid()) { + qCWarning(QT_BT_WINRT) << "characteristicChanged: Cannot find characteristic"; + return; + } + + // only update cache when property is readable. Otherwise it remains + // empty. + if (characteristic.properties() & QLowEnergyCharacteristic::Read) + updateValueOfCharacteristic(characteristic.attributeHandle(), + data, false); + emit service->characteristicChanged(characteristic, data); +} + +void QLowEnergyControllerPrivateWinRTNew::handleServiceHandlerError(const QString &error) +{ + if (state != QLowEnergyController::DiscoveringState) + return; + + qCWarning(QT_BT_WINRT) << "Error while discovering services:" << error; + setState(QLowEnergyController::UnconnectedState); + setError(QLowEnergyController::ConnectionError); +} + +QT_END_NAMESPACE + +#include "qlowenergycontroller_winrt_new.moc" |