summaryrefslogtreecommitdiffstats
path: root/src/bluetooth/qlowenergycontroller_winrt_new.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bluetooth/qlowenergycontroller_winrt_new.cpp')
-rw-r--r--src/bluetooth/qlowenergycontroller_winrt_new.cpp847
1 files changed, 479 insertions, 368 deletions
diff --git a/src/bluetooth/qlowenergycontroller_winrt_new.cpp b/src/bluetooth/qlowenergycontroller_winrt_new.cpp
index f951d1a6..a6371c0a 100644
--- a/src/bluetooth/qlowenergycontroller_winrt_new.cpp
+++ b/src/bluetooth/qlowenergycontroller_winrt_new.cpp
@@ -52,6 +52,7 @@
#include <QtCore/qfunctions_winrt.h>
#include <QtCore/QtEndian>
#include <QtCore/QLoggingCategory>
+#include <QtCore/QDeadlineTimer>
#include <private/qeventdispatcher_winrt_p.h>
#include <functional>
@@ -81,10 +82,16 @@ typedef ITypedEventHandler<GattCharacteristic *, GattValueChangedEventArgs *> Va
typedef GattReadClientCharacteristicConfigurationDescriptorResult ClientCharConfigDescriptorResult;
typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharConfigDescriptorResult;
-#define EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, ret) \
+#define EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr) \
if (FAILED(hr)) { \
emitErrorAndQuitThread(hr); \
- ret; \
+ return; \
+ }
+
+#define EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, message) \
+ if (FAILED(hr)) { \
+ emitErrorAndQuitThread(message); \
+ return; \
}
#define WARN_AND_CONTINUE_IF_FAILED(hr, msg) \
@@ -93,6 +100,13 @@ typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharCo
continue; \
}
+#define DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr, msg) \
+ if (FAILED(hr)) { \
+ qCWarning(QT_BT_WINRT) << msg; \
+ --mCharacteristicsCountToBeDiscovered; \
+ continue; \
+ }
+
#define CHECK_FOR_DEVICE_CONNECTION_ERROR_IMPL(this, hr, msg, ret) \
if (FAILED(hr)) { \
qCWarning(QT_BT_WINRT) << msg; \
@@ -115,6 +129,8 @@ typedef IGattReadClientCharacteristicConfigurationDescriptorResult IClientCharCo
Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT_SERVICE_THREAD)
+static constexpr qint64 kMaxConnectTimeout = 20000; // 20 sec
+
QLowEnergyControllerPrivate *createWinRTLowEnergyController()
{
if (supportsNewLEApi()) {
@@ -153,6 +169,7 @@ public:
~QWinRTLowEnergyServiceHandlerNew()
{
+ qCDebug(QT_BT_WINRT) << __FUNCTION__;
}
public slots:
@@ -163,24 +180,24 @@ public slots:
ComPtr<IAsyncOperation<GattCharacteristicsResult *>> characteristicsOp;
ComPtr<IGattCharacteristicsResult> characteristicsResult;
HRESULT hr = mDeviceService->GetCharacteristicsAsync(&characteristicsOp);
- EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr);
hr = QWinRTFunctions::await(characteristicsOp, characteristicsResult.GetAddressOf(),
QWinRTFunctions::ProcessMainThreadEvents, 5000);
- EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr);
GattCommunicationStatus status;
hr = characteristicsResult->get_Status(&status);
- EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr);
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);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr);
uint characteristicsCount;
hr = characteristics->get_Size(&characteristicsCount);
- EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr, return);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED(hr);
mCharacteristicsCountToBeDiscovered = characteristicsCount;
for (uint i = 0; i < characteristicsCount; ++i) {
@@ -204,185 +221,126 @@ public slots:
// 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";
+ ComPtr<IAsyncOperation<GattDescriptorsResult *>> descAsyncOp;
+ hr = characteristic3->GetDescriptorsAsync(&descAsyncOp);
+ DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr, "Could not obtain list of descriptors")
+
+ ComPtr<IGattDescriptorsResult> descResult;
+ hr = QWinRTFunctions::await(descAsyncOp, descResult.GetAddressOf());
+ DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr, "Could not obtain descriptor read result")
+
+ quint16 handle;
+ hr = characteristic->get_AttributeHandle(&handle);
+ DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(
+ hr, "Could not obtain characteristic's attribute handle")
+ 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);
+ DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr, "Could not obtain characteristic's Uuid")
+ charData.uuid = QBluetoothUuid(guuid);
+ GattCharacteristicProperties properties;
+ hr = characteristic->get_CharacteristicProperties(&properties);
+ DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr,
+ "Could not obtain characteristic's properties")
+ charData.properties = QLowEnergyCharacteristic::PropertyTypes(properties & 0xff);
+ if (charData.properties & QLowEnergyCharacteristic::Read) {
+ ComPtr<IAsyncOperation<GattReadResult *>> readOp;
+ hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached,
+ &readOp);
+ DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr, "Could not read characteristic")
+ ComPtr<IGattReadResult> readResult;
+ hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf());
+ DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr,
+ "Could not obtain characteristic read result")
+ if (!readResult)
+ qCWarning(QT_BT_WINRT) << "Characteristic read result is null";
+ else
+ charData.value = byteArrayFromGattResult(readResult);
+ }
+ mCharacteristicList.insert(handle, charData);
+
+ ComPtr<IVectorView<GattDescriptor *>> descriptors;
+
+ GattCommunicationStatus commStatus;
+ hr = descResult->get_Status(&commStatus);
+ if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
+ qCWarning(QT_BT_WINRT) << "Descriptor operation failed";
--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;
+ hr = descResult->get_Descriptors(&descriptors);
+ DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr, "Could not obtain list of descriptors")
+
+ uint descriptorCount;
+ hr = descriptors->get_Size(&descriptorCount);
+ DEC_CHAR_COUNT_AND_CONTINUE_IF_FAILED(hr, "Could not obtain list of descriptors' size")
+ 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::DescriptorType::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());
- 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";
+ WARN_AND_CONTINUE_IF_FAILED(hr, "Could await descriptor read result")
+ if (descData.uuid == QBluetoothUuid::DescriptorType::CharacteristicUserDescription)
+ descData.value = byteArrayFromGattResult(readResult, true);
else
- charData.value = byteArrayFromGattResult(readResult);
+ descData.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;
+ charData.descriptorList.insert(descHandle, descData);
}
+
+ mCharacteristicList.insert(handle, charData);
+ --mCharacteristicsCountToBeDiscovered;
}
checkAllCharacteristicsDiscovered();
}
@@ -432,6 +390,292 @@ void QWinRTLowEnergyServiceHandlerNew::emitErrorAndQuitThread(const QString &err
QThread::currentThread()->quit();
}
+class QWinRTLowEnergyConnectionHandler : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QWinRTLowEnergyConnectionHandler(const QBluetoothAddress &address) : mAddress(address)
+ {
+ qCDebug(QT_BT_WINRT) << __FUNCTION__;
+ // This should be checked before the handler is created
+ Q_ASSERT(!mAddress.isNull());
+ qRegisterMetaType<ComPtr<IBluetoothLEDevice>>("ComPtr<IBluetoothLEDevice>");
+ }
+ ~QWinRTLowEnergyConnectionHandler()
+ {
+ qCDebug(QT_BT_WINRT) << __FUNCTION__;
+ // To close the COM library gracefully, each successful call to
+ // CoInitialize, including those that return S_FALSE, must be balanced
+ // by a corresponding call to CoUninitialize.
+ if (mInitialized == S_OK || mInitialized == S_FALSE)
+ CoUninitialize();
+ }
+
+public slots:
+ void connectToDevice();
+ void handleDeviceDisconnectRequest();
+
+signals:
+ void deviceConnected(ComPtr<IBluetoothLEDevice> device);
+ void errorOccurred(const QString &error);
+
+private:
+ void connectToPairedDevice();
+ void connectToUnpairedDevice();
+ void emitErrorAndQuitThread(const QString &error);
+ void emitErrorAndQuitThread(const char *error);
+ void emitConnectedAndQuitThread();
+
+ ComPtr<IBluetoothLEDevice> mDevice = nullptr;
+ ComPtr<IGattSession> mGattSession = nullptr;
+ const QBluetoothAddress mAddress;
+ bool mAbortConnection = false;
+ HRESULT mInitialized = E_UNEXPECTED; // some error code
+};
+
+void QWinRTLowEnergyConnectionHandler::connectToDevice()
+{
+ qCDebug(QT_BT_WINRT) << __FUNCTION__;
+ mInitialized = CoInitialize(NULL);
+ qCDebug(QT_BT_WINRT) << qt_error_string(mInitialized);
+
+ auto earlyExit = [this]() { return mAbortConnection; };
+ ComPtr<IBluetoothLEDeviceStatics> deviceStatics;
+ HRESULT hr = GetActivationFactory(
+ HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_BluetoothLEDevice).Get(),
+ &deviceStatics);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain device factory");
+ ComPtr<IAsyncOperation<BluetoothLEDevice *>> deviceFromIdOperation;
+ hr = deviceStatics->FromBluetoothAddressAsync(mAddress.toUInt64(), &deviceFromIdOperation);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not find LE device from address");
+ hr = QWinRTFunctions::await(deviceFromIdOperation, mDevice.GetAddressOf(),
+ QWinRTFunctions::ProcessMainThreadEvents, 5000, earlyExit);
+ if (FAILED(hr) || !mDevice) {
+ emitErrorAndQuitThread("Could not find LE device");
+ return;
+ }
+
+ // get GattSession: 1. get device id
+ ComPtr<IBluetoothLEDevice4> device4;
+ hr = mDevice.As(&device4);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not cast device");
+
+ ComPtr<IBluetoothDeviceId> deviceId;
+ hr = device4->get_BluetoothDeviceId(&deviceId);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not get bluetooth device id");
+
+ // get GattSession: 2. get session statics
+ ComPtr<IGattSessionStatics> sessionStatics;
+ hr = GetActivationFactory(
+ HString::MakeReference(
+ RuntimeClass_Windows_Devices_Bluetooth_GenericAttributeProfile_GattSession)
+ .Get(),
+ &sessionStatics);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain GattSession statics");
+
+ // get GattSession: 3. get session
+ ComPtr<IAsyncOperation<GattSession *>> gattSessionFromIdOperation;
+ hr = sessionStatics->FromDeviceIdAsync(deviceId.Get(), &gattSessionFromIdOperation);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not get GattSession from id");
+ hr = QWinRTFunctions::await(gattSessionFromIdOperation, mGattSession.GetAddressOf(),
+ QWinRTFunctions::ProcessMainThreadEvents, 5000, earlyExit);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not complete Gatt session acquire");
+
+ BluetoothConnectionStatus status;
+ hr = mDevice->get_ConnectionStatus(&status);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain device's connection status");
+ if (status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) {
+ emitConnectedAndQuitThread();
+ return;
+ }
+
+ QBluetoothLocalDevice localDevice;
+ QBluetoothLocalDevice::Pairing pairing = localDevice.pairingStatus(mAddress);
+ if (pairing == QBluetoothLocalDevice::Unpaired)
+ connectToUnpairedDevice();
+ else
+ connectToPairedDevice();
+}
+
+void QWinRTLowEnergyConnectionHandler::handleDeviceDisconnectRequest()
+{
+ mAbortConnection = true;
+ // Disconnect from the QLowEnergyControllerPrivateWinRT, so that it does
+ // not get notifications. It's absolutely fine to keep doing smth in
+ // background, as multiple connections to the same device should be handled
+ // correctly by OS.
+ disconnect(this, &QWinRTLowEnergyConnectionHandler::deviceConnected, nullptr, nullptr);
+ disconnect(this, &QWinRTLowEnergyConnectionHandler::errorOccurred, nullptr, nullptr);
+}
+
+void QWinRTLowEnergyConnectionHandler::connectToPairedDevice()
+{
+ qCDebug(QT_BT_WINRT) << __FUNCTION__;
+ ComPtr<IBluetoothLEDevice3> device3;
+ HRESULT hr = mDevice.As(&device3);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not cast device");
+ ComPtr<IAsyncOperation<GattDeviceServicesResult *>> deviceServicesOp;
+ auto earlyExit = [this]() { return mAbortConnection; };
+ QDeadlineTimer deadline(kMaxConnectTimeout);
+ while (!mAbortConnection && !deadline.hasExpired()) {
+ hr = device3->GetGattServicesAsync(&deviceServicesOp);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain services");
+ ComPtr<IGattDeviceServicesResult> deviceServicesResult;
+ hr = QWinRTFunctions::await(deviceServicesOp, deviceServicesResult.GetAddressOf(),
+ QWinRTFunctions::ProcessThreadEvents, 5000, earlyExit);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not await services operation");
+
+ GattCommunicationStatus commStatus;
+ hr = deviceServicesResult->get_Status(&commStatus);
+ if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
+ emitErrorAndQuitThread("Service operation failed");
+ return;
+ }
+
+ ComPtr<IVectorView<GattDeviceService *>> deviceServices;
+ hr = deviceServicesResult->get_Services(&deviceServices);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain list of services");
+ uint serviceCount;
+ hr = deviceServices->get_Size(&serviceCount);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain service count");
+
+ if (serviceCount == 0) {
+ emitErrorAndQuitThread("Found devices without services");
+ 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);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain service");
+ ComPtr<IGattDeviceService3> service3;
+ hr = service.As(&service3);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not cast service");
+ ComPtr<IAsyncOperation<GattCharacteristicsResult *>> characteristicsOp;
+ hr = service3->GetCharacteristicsAsync(&characteristicsOp);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain characteristic");
+ ComPtr<IGattCharacteristicsResult> characteristicsResult;
+ hr = QWinRTFunctions::await(characteristicsOp, characteristicsResult.GetAddressOf(),
+ QWinRTFunctions::ProcessThreadEvents, 5000, earlyExit);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(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";
+ break;
+ }
+ 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.
+ emitErrorAndQuitThread("Could not obtain characteristic list. "
+ "Please check your manifest capabilities");
+ return;
+ }
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain characteristic list");
+ uint characteristicsCount;
+ hr = characteristics->get_Size(&characteristicsCount);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr,
+ "Could not obtain characteristic list's size");
+ for (uint j = 0; j < characteristicsCount; ++j) {
+ ComPtr<IGattCharacteristic> characteristic;
+ hr = characteristics->GetAt(j, &characteristic);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain characteristic");
+ ComPtr<IAsyncOperation<GattReadResult *>> op;
+ GattCharacteristicProperties props;
+ hr = characteristic->get_CharacteristicProperties(&props);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(
+ hr, "Could not obtain characteristic's properties");
+ if (!(props & GattCharacteristicProperties_Read))
+ continue;
+ hr = characteristic->ReadValueWithCacheModeAsync(
+ BluetoothCacheMode::BluetoothCacheMode_Uncached, &op);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not read characteristic value");
+ ComPtr<IGattReadResult> result;
+ // Reading characteristics can take surprisingly long at the
+ // first time, so we need to have a large the timeout here.
+ hr = QWinRTFunctions::await(op, result.GetAddressOf(),
+ QWinRTFunctions::ProcessThreadEvents, 5000, earlyExit);
+ // E_ILLEGAL_METHOD_CALL will be the result for a device, that is not reachable at
+ // the moment. In this case we should jump back into the outer loop and keep trying.
+ if (hr == E_ILLEGAL_METHOD_CALL)
+ break;
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not await characteristic read");
+ ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
+ hr = result->get_Value(&buffer);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain characteristic value");
+ if (!buffer) {
+ qCDebug(QT_BT_WINRT) << "Problem reading value";
+ break;
+ }
+
+ emitConnectedAndQuitThread();
+ return;
+ }
+ }
+ }
+ // If we got here because of mAbortConnection == true, the error message
+ // will not be delivered, so it does not matter. But we need to terminate
+ // the thread anyway!
+ emitErrorAndQuitThread("Connect to device failed due to timeout!");
+}
+
+void QWinRTLowEnergyConnectionHandler::connectToUnpairedDevice()
+{
+ qCDebug(QT_BT_WINRT) << __FUNCTION__;
+ ComPtr<IBluetoothLEDevice3> device3;
+ HRESULT hr = mDevice.As(&device3);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not cast device");
+ ComPtr<IGattDeviceServicesResult> deviceServicesResult;
+ auto earlyExit = [this]() { return mAbortConnection; };
+ QDeadlineTimer deadline(kMaxConnectTimeout);
+ while (!mAbortConnection && !deadline.hasExpired()) {
+ ComPtr<IAsyncOperation<GattDeviceServicesResult *>> deviceServicesOp;
+ hr = device3->GetGattServicesAsync(&deviceServicesOp);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not obtain services");
+ hr = QWinRTFunctions::await(deviceServicesOp, deviceServicesResult.GetAddressOf(),
+ QWinRTFunctions::ProcessMainThreadEvents, 0, earlyExit);
+ EMIT_WORKER_ERROR_AND_QUIT_IF_FAILED_2(hr, "Could not await services operation");
+
+ GattCommunicationStatus commStatus;
+ hr = deviceServicesResult->get_Status(&commStatus);
+ if (commStatus == GattCommunicationStatus_Unreachable)
+ continue;
+
+ if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
+ emitErrorAndQuitThread("Service operation failed");
+ return;
+ }
+
+ emitConnectedAndQuitThread();
+ return;
+ }
+ // If we got here because of mAbortConnection == true, the error message
+ // will not be delivered, so it does not matter. But we need to terminate
+ // the thread anyway!
+ emitErrorAndQuitThread("Connect to device failed due to timeout!");
+}
+
+void QWinRTLowEnergyConnectionHandler::emitErrorAndQuitThread(const QString &error)
+{
+ emit errorOccurred(error);
+ QThread::currentThread()->quit();
+}
+
+void QWinRTLowEnergyConnectionHandler::emitErrorAndQuitThread(const char *error)
+{
+ emitErrorAndQuitThread(QString::fromUtf8(error));
+}
+
+void QWinRTLowEnergyConnectionHandler::emitConnectedAndQuitThread()
+{
+ emit deviceConnected(mDevice);
+ QThread::currentThread()->quit();
+}
+
QLowEnergyControllerPrivateWinRTNew::QLowEnergyControllerPrivateWinRTNew()
: QLowEnergyControllerPrivate()
{
@@ -445,7 +689,6 @@ QLowEnergyControllerPrivateWinRTNew::~QLowEnergyControllerPrivateWinRTNew()
{
unregisterFromStatusChanges();
unregisterFromValueChanges();
- mAbortPending = true;
}
void QLowEnergyControllerPrivateWinRTNew::init()
@@ -455,47 +698,39 @@ void QLowEnergyControllerPrivateWinRTNew::init()
void QLowEnergyControllerPrivateWinRTNew::connectToDevice()
{
qCDebug(QT_BT_WINRT) << __FUNCTION__;
- mAbortPending = false;
- 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)
- if (status == BluetoothConnectionStatus::BluetoothConnectionStatus_Connected) {
- setState(QLowEnergyController::ConnectedState);
- emit q->connected();
- return;
- }
-
- QBluetoothLocalDevice localDevice;
- QBluetoothLocalDevice::Pairing pairing = localDevice.pairingStatus(remoteDevice);
- if (pairing == QBluetoothLocalDevice::Unpaired)
- connectToUnpairedDevice();
- else
- connectToPairedDevice();
+ QWinRTLowEnergyConnectionHandler *worker = new QWinRTLowEnergyConnectionHandler(remoteDevice);
+ QThread *thread = new QThread;
+ worker->moveToThread(thread);
+ connect(this, &QLowEnergyControllerPrivateWinRTNew::abortConnection, worker,
+ &QWinRTLowEnergyConnectionHandler::handleDeviceDisconnectRequest);
+ connect(thread, &QThread::started, worker, &QWinRTLowEnergyConnectionHandler::connectToDevice);
+ connect(thread, &QThread::finished, thread, &QObject::deleteLater);
+ connect(thread, &QThread::finished, worker, &QObject::deleteLater);
+ connect(worker, &QWinRTLowEnergyConnectionHandler::errorOccurred, this,
+ [this](const QString &msg) { handleConnectionError(msg.toUtf8().constData()); });
+ connect(worker, &QWinRTLowEnergyConnectionHandler::deviceConnected, this,
+ [this](ComPtr<IBluetoothLEDevice> device) {
+ if (!device) {
+ handleConnectionError("Failed to get device");
+ return;
+ }
+ mDevice = device;
+ if (!registerForStatusChanges()) {
+ handleConnectionError("Failed to register for changes");
+ return;
+ }
+ Q_Q(QLowEnergyController);
+ setState(QLowEnergyController::ConnectedState);
+ emit q->connected();
+ });
+ thread->start();
}
void QLowEnergyControllerPrivateWinRTNew::disconnectFromDevice()
@@ -503,9 +738,9 @@ void QLowEnergyControllerPrivateWinRTNew::disconnectFromDevice()
qCDebug(QT_BT_WINRT) << __FUNCTION__;
Q_Q(QLowEnergyController);
setState(QLowEnergyController::ClosingState);
+ emit abortConnection();
unregisterFromValueChanges();
unregisterFromStatusChanges();
- mAbortPending = true;
mDevice = nullptr;
setState(QLowEnergyController::UnconnectedState);
emit q->disconnected();
@@ -689,6 +924,9 @@ void QLowEnergyControllerPrivateWinRTNew::obtainIncludedServices(
ComPtr<IGattDeviceServicesResult> result;
hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessMainThreadEvents, 5000);
RETURN_IF_FAILED("Could not await service operation", return);
+ // The device can be disconnected by the time we return from await()
+ if (state != QLowEnergyController::DiscoveringState)
+ return;
GattCommunicationStatus status;
hr = result->get_Status(&status);
if (FAILED(hr) || status != GattCommunicationStatus_Success) {
@@ -735,6 +973,15 @@ void QLowEnergyControllerPrivateWinRTNew::obtainIncludedServices(
HRESULT QLowEnergyControllerPrivateWinRTNew::onServiceDiscoveryFinished(ABI::Windows::Foundation::IAsyncOperation<GattDeviceServicesResult *> *op, AsyncStatus status)
{
+ // Check if the device is in the proper state, because it can already be
+ // disconnected when the callback arrives.
+ // Also the callback can theoretically come when the connection is
+ // reestablisheed again (for example, if the user quickly clicks
+ // "Disconnect" and then "Connect" again in some UI). But we can probably
+ // omit such details, as we are connecting to the same device anyway.
+ if (state != QLowEnergyController::DiscoveringState)
+ return S_OK;
+
Q_Q(QLowEnergyController);
if (status != AsyncStatus::Completed) {
qCDebug(QT_BT_WINRT) << "Could not obtain services";
@@ -786,6 +1033,14 @@ HRESULT QLowEnergyControllerPrivateWinRTNew::onServiceDiscoveryFinished(ABI::Win
pointer->type |= QLowEnergyService::PrimaryService;
obtainIncludedServices(pointer, deviceService);
+ // The obtainIncludedServices method calls await(), so the device can be
+ // disconnected by the time we return from it. TODO - rewrite in an
+ // async way!
+ if (state != QLowEnergyController::DiscoveringState) {
+ emit q->discoveryFinished(); // Probably not needed when the device
+ // is already disconnected?
+ return S_OK;
+ }
emit q->serviceDiscovered(service);
}
@@ -920,8 +1175,8 @@ void QLowEnergyControllerPrivateWinRTNew::discoverServiceDetails(const QBluetoot
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();
+ qCWarning(QT_BT_WINRT)
+ << "Discovery complete for unknown service:" << service.toString();
return;
}
@@ -1308,7 +1563,7 @@ void QLowEnergyControllerPrivateWinRTNew::writeCharacteristic(
}
// only update cache when property is readable. Otherwise it remains
// empty.
- if (charData.properties & QLowEnergyCharacteristic::Read)
+ if (thisPtr && charData.properties & QLowEnergyCharacteristic::Read)
thisPtr->updateValueOfCharacteristic(charHandle, newValue, false);
if (writeWithResponse)
emit service->characteristicWritten(QLowEnergyCharacteristic(service, charHandle),
@@ -1410,7 +1665,8 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor(
service->setError(QLowEnergyService::DescriptorWriteError);
return S_OK;
}
- thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue, false);
+ if (thisPtr)
+ thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue, false);
emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
newValue);
return S_OK;
@@ -1504,7 +1760,8 @@ void QLowEnergyControllerPrivateWinRTNew::writeDescriptor(
service->setError(QLowEnergyService::DescriptorWriteError);
return S_OK;
}
- thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue, false);
+ if (thisPtr)
+ thisPtr->updateValueOfDescriptor(charHandle, descHandle, newValue, false);
emit service->descriptorWritten(QLowEnergyDescriptor(service, charHandle, descHandle),
newValue);
return S_OK;
@@ -1567,158 +1824,12 @@ void QLowEnergyControllerPrivateWinRTNew::handleServiceHandlerError(const QStrin
setError(QLowEnergyController::ConnectionError);
}
-void QLowEnergyControllerPrivateWinRTNew::connectToPairedDevice()
-{
- Q_Q(QLowEnergyController);
- ComPtr<IBluetoothLEDevice3> device3;
- HRESULT hr = mDevice.As(&device3);
- CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast device", return)
- ComPtr<IAsyncOperation<GattDeviceServicesResult *>> deviceServicesOp;
- while (!mAbortPending) {
- 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::ProcessThreadEvents, 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)
-
- if (serviceCount == 0) {
- qCWarning(QT_BT_WINRT()) << "Found devices without services";
- setError(QLowEnergyController::ConnectionError);
- setState(QLowEnergyController::UnconnectedState);
- unregisterFromStatusChanges();
- 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);
- CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain service", return);
- ComPtr<IGattDeviceService3> service3;
- hr = service.As(&service3);
- CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast service", return);
- ComPtr<IAsyncOperation<GattCharacteristicsResult *>> characteristicsOp;
- hr = service3->GetCharacteristicsAsync(&characteristicsOp);
- CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic", return);
- ComPtr<IGattCharacteristicsResult> characteristicsResult;
- hr = QWinRTFunctions::await(characteristicsOp, characteristicsResult.GetAddressOf(),
- QWinRTFunctions::ProcessThreadEvents, 5000);
- CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await characteristic operation", return);
- GattCommunicationStatus commStatus;
- hr = characteristicsResult->get_Status(&commStatus);
- if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
- qCWarning(QT_BT_WINRT) << "Characteristic operation failed";
- break;
- }
- 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;
- }
- CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic list", return);
- uint characteristicsCount;
- hr = characteristics->get_Size(&characteristicsCount);
- CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic list's size", return);
- for (uint j = 0; j < characteristicsCount; ++j) {
- ComPtr<IGattCharacteristic> characteristic;
- hr = characteristics->GetAt(j, &characteristic);
- CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic", return);
- ComPtr<IAsyncOperation<GattReadResult *>> op;
- GattCharacteristicProperties props;
- hr = characteristic->get_CharacteristicProperties(&props);
- CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic's properties", return);
- if (!(props & GattCharacteristicProperties_Read))
- continue;
- hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode::BluetoothCacheMode_Uncached, &op);
- CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not read characteristic value", return);
- ComPtr<IGattReadResult> result;
- hr = QWinRTFunctions::await(op, result.GetAddressOf(), QWinRTFunctions::ProcessThreadEvents, 500);
- // E_ILLEGAL_METHOD_CALL will be the result for a device, that is not reachable at
- // the moment. In this case we should jump back into the outer loop and keep trying.
- if (hr == E_ILLEGAL_METHOD_CALL)
- break;
- CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await characteristic read", return);
- ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
- hr = result->get_Value(&buffer);
- CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain characteristic value", return);
- if (!buffer) {
- qCDebug(QT_BT_WINRT) << "Problem reading value";
- break;
- }
-
- setState(QLowEnergyController::ConnectedState);
- emit q->connected();
- if (!registerForStatusChanges()) {
- setError(QLowEnergyController::ConnectionError);
- setState(QLowEnergyController::UnconnectedState);
- return;
- }
- return;
- }
- }
- }
-}
-
-void QLowEnergyControllerPrivateWinRTNew::connectToUnpairedDevice()
+void QLowEnergyControllerPrivateWinRTNew::handleConnectionError(const char *logMessage)
{
- if (!registerForStatusChanges()) {
- setError(QLowEnergyController::ConnectionError);
- setState(QLowEnergyController::UnconnectedState);
- return;
- }
- ComPtr<IBluetoothLEDevice3> device3;
- HRESULT hr = mDevice.As(&device3);
- CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not cast device", return)
- ComPtr<IGattDeviceServicesResult> deviceServicesResult;
- while (!mAbortPending) {
- ComPtr<IAsyncOperation<GattDeviceServicesResult *>> deviceServicesOp;
- hr = device3->GetGattServicesAsync(&deviceServicesOp);
- CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not obtain services", return)
- hr = QWinRTFunctions::await(deviceServicesOp, deviceServicesResult.GetAddressOf(),
- QWinRTFunctions::ProcessMainThreadEvents);
- CHECK_FOR_DEVICE_CONNECTION_ERROR(hr, "Could not await services operation", return)
-
- GattCommunicationStatus commStatus;
- hr = deviceServicesResult->get_Status(&commStatus);
- if (commStatus == GattCommunicationStatus_Unreachable)
- continue;
-
- if (FAILED(hr) || commStatus != GattCommunicationStatus_Success) {
- qCWarning(QT_BT_WINRT()) << "Service operation failed";
- setError(QLowEnergyController::ConnectionError);
- setState(QLowEnergyController::UnconnectedState);
- unregisterFromStatusChanges();
- return;
- }
-
- break;
- }
+ qCWarning(QT_BT_WINRT) << logMessage;
+ setError(QLowEnergyController::ConnectionError);
+ setState(QLowEnergyController::UnconnectedState);
+ unregisterFromStatusChanges();
}
QT_END_NAMESPACE