diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp index 6786ac5..9392878 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_winrt.cpp @@ -101,11 +101,7 @@ private: HRESULT onPairedClassicBluetoothDeviceFoundAsync(IAsyncOperation *op, AsyncStatus status ); HRESULT onPairedBluetoothLEDeviceFoundAsync(IAsyncOperation *op, AsyncStatus status); HRESULT onBluetoothLEDeviceFoundAsync(IAsyncOperation *op, AsyncStatus status); - enum PairingCheck { - CheckForPairing, - OmitPairingCheck - }; - HRESULT onBluetoothLEDeviceFound(ComPtr device, PairingCheck pairingCheck = CheckForPairing); + HRESULT onBluetoothLEDeviceFound(ComPtr device); public slots: void finishDiscovery(); @@ -120,7 +116,8 @@ public: private: ComPtr m_leWatcher; EventRegistrationToken m_leDeviceAddedToken; - QVector m_foundLEDevices; + QMutex m_foundDevicesMutex; + QMap> m_foundLEDevices; int m_pendingPairedDevices; ComPtr m_deviceStatics; @@ -240,15 +237,51 @@ void QWinRTBluetoothDeviceDiscoveryWorker::setupLEDeviceWatcher() { HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisementWatcher).Get(), &m_leWatcher); Q_ASSERT_SUCCEEDED(hr); + hr = m_leWatcher->put_ScanningMode(BluetoothLEScanningMode_Active); + Q_ASSERT_SUCCEEDED(hr); hr = m_leWatcher->add_Received(Callback>([this](IBluetoothLEAdvertisementWatcher *, IBluetoothLEAdvertisementReceivedEventArgs *args) { quint64 address; HRESULT hr; hr = args->get_BluetoothAddress(&address); Q_ASSERT_SUCCEEDED(hr); - if (m_foundLEDevices.contains(address)) - return S_OK; + ComPtr ad; + args->get_Advertisement(&ad); + Q_ASSERT_SUCCEEDED(hr); + ComPtr> guids; + hr = ad->get_ServiceUuids(&guids); + Q_ASSERT_SUCCEEDED(hr); + quint32 size; + hr = guids->get_Size(&size); + Q_ASSERT_SUCCEEDED(hr); + QVector serviceUuids; + for (quint32 i = 0; i < size; ++i) { + GUID guid; + hr = guids.Get()->GetAt(i, &guid); + Q_ASSERT_SUCCEEDED(hr); + QBluetoothUuid uuid(guid); + serviceUuids.append(uuid); + } + QMutexLocker locker(&m_foundDevicesMutex); + // Merge newly found services with list of currently found ones + if (m_foundLEDevices.contains(address)) { + if (size == 0) + return S_OK; + QVector foundServices = m_foundLEDevices.value(address); + bool newServiceAdded = false; + for (const QBluetoothUuid uuid : qAsConst(serviceUuids)) { + if (!foundServices.contains(uuid)) { + foundServices.append(uuid); + newServiceAdded = true; + } + } + if (!newServiceAdded) + return S_OK; + m_foundLEDevices[address] = foundServices; + } else { + m_foundLEDevices.insert(address, serviceUuids); + } - m_foundLEDevices.append(address); + locker.unlock(); leBluetoothInfoFromAddressAsync(address); return S_OK; }).Get(), &m_leDeviceAddedToken); @@ -417,7 +450,7 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onPairedBluetoothLEDeviceFoundAsyn HRESULT hr; hr = op->GetResults(&device); Q_ASSERT_SUCCEEDED(hr); - return onBluetoothLEDeviceFound(device, OmitPairingCheck); + return onBluetoothLEDeviceFound(device); } HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFoundAsync(IAsyncOperation *op, AsyncStatus status) @@ -429,66 +462,16 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFoundAsync(IAsy HRESULT hr; hr = op->GetResults(&device); Q_ASSERT_SUCCEEDED(hr); - return onBluetoothLEDeviceFound(device, PairingCheck::CheckForPairing); + return onBluetoothLEDeviceFound(device); } -HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr device, PairingCheck pairingCheck) +HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtr device) { if (!device) { qCDebug(QT_BT_WINRT) << "onBluetoothLEDeviceFound: No device given"; return S_OK; } - if (pairingCheck == CheckForPairing) { - ComPtr device2; - HRESULT hr = device.As(&device2); - Q_ASSERT_SUCCEEDED(hr); - ComPtr deviceInfo; - hr = device2->get_DeviceInformation(&deviceInfo); - Q_ASSERT_SUCCEEDED(hr); - if (!deviceInfo) { - qCDebug(QT_BT_WINRT) << "onBluetoothLEDeviceFound: Could not obtain device information"; - return S_OK; - } - ComPtr deviceInfo2; - hr = deviceInfo.As(&deviceInfo2); - Q_ASSERT_SUCCEEDED(hr); - ComPtr pairing; - hr = deviceInfo2->get_Pairing(&pairing); - Q_ASSERT_SUCCEEDED(hr); - boolean isPaired; - hr = pairing->get_IsPaired(&isPaired); - Q_ASSERT_SUCCEEDED(hr); - // We need a paired device in order to be able to obtain its information - if (!isPaired) { - ComPtr> pairingOp; - hr = pairing.Get()->PairAsync(&pairingOp); - Q_ASSERT_SUCCEEDED(hr); - pairingOp.Get()->put_Completed( - Callback>([device, this](IAsyncOperation *op, AsyncStatus status) { - if (status != AsyncStatus::Completed) { - qCDebug(QT_BT_WINRT) << "Could not pair device"; - return S_OK; - } - - ComPtr result; - op->GetResults(&result); - - DevicePairingResultStatus pairingStatus; - result.Get()->get_Status(&pairingStatus); - - if (pairingStatus != DevicePairingResultStatus_Paired) { - qCDebug(QT_BT_WINRT) << "Could not pair device"; - return S_OK; - } - - onBluetoothLEDeviceFound(device, OmitPairingCheck); - return S_OK; - }).Get()); - return S_OK; - } - } - UINT64 address; HString name; HRESULT hr = device->get_BluetoothAddress(&address); @@ -496,22 +479,67 @@ HRESULT QWinRTBluetoothDeviceDiscoveryWorker::onBluetoothLEDeviceFound(ComPtrget_Name(name.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); const QString btName = QString::fromWCharArray(WindowsGetStringRawBuffer(name.Get(), nullptr)); - IVectorView *deviceServices; - hr = device->get_GattServices(&deviceServices); + + ComPtr device2; + hr = device.As(&device2); Q_ASSERT_SUCCEEDED(hr); - uint serviceCount; - hr = deviceServices->get_Size(&serviceCount); + ComPtr deviceInfo; + hr = device2->get_DeviceInformation(&deviceInfo); + Q_ASSERT_SUCCEEDED(hr); + if (!deviceInfo) { + qCDebug(QT_BT_WINRT) << "onBluetoothLEDeviceFound: Could not obtain device information"; + return S_OK; + } + ComPtr deviceInfo2; + hr = deviceInfo.As(&deviceInfo2); + Q_ASSERT_SUCCEEDED(hr); + ComPtr pairing; + hr = deviceInfo2->get_Pairing(&pairing); + Q_ASSERT_SUCCEEDED(hr); + boolean isPaired; + hr = pairing->get_IsPaired(&isPaired); Q_ASSERT_SUCCEEDED(hr); QList uuids; - for (uint i = 0; i < serviceCount; ++i) { - ComPtr service; - hr = deviceServices->GetAt(i, &service); + + // Use the services obtained from the advertisement data if the device is not paired + if (!isPaired) { + uuids = m_foundLEDevices.value(address).toList(); + + // Alternative way is to discover all services with new async API. + // It can be done without pairing but connection will still be established to the remote device, + // Which is not the best solution for just scanning for advertisements. +// if(serviceCount == 0){ +// ComPtr device3; +// hr = device.As(&device3); +// Q_ASSERT_SUCCEEDED(hr); +// ComPtr> asyncResult; +// hr = device3->GetGattServicesAsync(&asyncResult); +// Q_ASSERT_SUCCEEDED(hr); +// hr = asyncResult->put_Completed( +// Callback>([this, device](IAsyncOperation *op, AsyncStatus) { +// qDebug() << "DISCO finished!"; +// onBluetoothLEDeviceFound(device, OmitPairingCheck); +// return S_OK; +// }).Get()); +// return S_OK; +// } + } else { + IVectorView *deviceServices; + hr = device->get_GattServices(&deviceServices); Q_ASSERT_SUCCEEDED(hr); - ComPtr id; - GUID uuid; - hr = service->get_Uuid(&uuid); + uint serviceCount; + hr = deviceServices->get_Size(&serviceCount); Q_ASSERT_SUCCEEDED(hr); - uuids.append(QBluetoothUuid(uuid)); + for (uint i = 0; i < serviceCount; ++i) { + ComPtr service; + hr = deviceServices->GetAt(i, &service); + Q_ASSERT_SUCCEEDED(hr); + ComPtr id; + GUID uuid; + hr = service->get_Uuid(&uuid); + Q_ASSERT_SUCCEEDED(hr); + uuids.append(QBluetoothUuid(uuid)); + } } qCDebug(QT_BT_WINRT) << "Discovered BTLE device: " << QString::number(address) << btName diff --git a/src/bluetooth/qlowenergycontroller_winrt.cpp b/src/bluetooth/qlowenergycontroller_winrt.cpp index c9c30b4..ba245bf 100644 --- a/src/bluetooth/qlowenergycontroller_winrt.cpp +++ b/src/bluetooth/qlowenergycontroller_winrt.cpp @@ -161,112 +161,163 @@ public slots: uint characteristicsCount; hr = characteristics->get_Size(&characteristicsCount); + + // If there are no characteristics, we assume that the device is not paired (and not discovered by Windows) + // And we use new API (GetCharacteristicsAsync) to discover them without pairing. + if(characteristicsCount == 0){ + ComPtr deviceService3; + hr = mDeviceService.As(&deviceService3); + Q_ASSERT_SUCCEEDED(hr); + ComPtr> asyncResult; + deviceService3->GetCharacteristicsAsync(&asyncResult); + hr = asyncResult->put_Completed( + Callback>([this](IAsyncOperation *op, AsyncStatus) { + // TODO We should check if we found any characteristics. It makes no sense but + // there is a possibility that device doesn't state any characteristics under a service. + // So, for sanity, we should not continue endless loop here. + obtainCharList(); + return S_OK; + }).Get()); + return; + } + Q_ASSERT_SUCCEEDED(hr); + mCharacteristicsCountToBeDiscovered = characteristicsCount; for (uint i = 0; i < characteristicsCount; ++i) { ComPtr characteristic; hr = characteristics->GetAt(i, &characteristic); Q_ASSERT_SUCCEEDED(hr); - quint16 handle; - hr = characteristic->get_AttributeHandle(&handle); - Q_ASSERT_SUCCEEDED(hr); - QLowEnergyServicePrivate::CharData charData; - charData.valueHandle = handle + 1; - if (startHandle == 0 || startHandle > handle) - startHandle = handle; - if (endHandle == 0 || endHandle < handle) - endHandle = handle; - GUID guuid; - hr = characteristic->get_Uuid(&guuid); + + ComPtr characteristic3; + hr = characteristic.As(&characteristic3); Q_ASSERT_SUCCEEDED(hr); - charData.uuid = QBluetoothUuid(guuid); - GattCharacteristicProperties properties; - hr = characteristic->get_CharacteristicProperties(&properties); + + // 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> descAsyncResult; + hr = characteristic3->GetDescriptorsAsync(&descAsyncResult); Q_ASSERT_SUCCEEDED(hr); - charData.properties = QLowEnergyCharacteristic::PropertyTypes(properties); - if (charData.properties & QLowEnergyCharacteristic::Read) { - ComPtr> readOp; - hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp); + hr = descAsyncResult->put_Completed( + Callback>([this, characteristic](IAsyncOperation *op, AsyncStatus) { + quint16 handle; + + HRESULT hr = characteristic->get_AttributeHandle(&handle); Q_ASSERT_SUCCEEDED(hr); - ComPtr readResult; - hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); + 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); Q_ASSERT_SUCCEEDED(hr); - if (readResult) - charData.value = byteArrayFromGattResult(readResult); - } - ComPtr characteristic2; - hr = characteristic.As(&characteristic2); - Q_ASSERT_SUCCEEDED(hr); - ComPtr> descriptors; - hr = characteristic2->GetAllDescriptors(&descriptors); - Q_ASSERT_SUCCEEDED(hr); - uint descriptorCount; - hr = descriptors->get_Size(&descriptorCount); - Q_ASSERT_SUCCEEDED(hr); - for (uint j = 0; j < descriptorCount; ++j) { - QLowEnergyServicePrivate::DescData descData; - ComPtr descriptor; - hr = descriptors->GetAt(j, &descriptor); + charData.uuid = QBluetoothUuid(guuid); + GattCharacteristicProperties properties; + hr = characteristic->get_CharacteristicProperties(&properties); Q_ASSERT_SUCCEEDED(hr); - quint16 descHandle; - hr = descriptor->get_AttributeHandle(&descHandle); + charData.properties = QLowEnergyCharacteristic::PropertyTypes(properties); + if (charData.properties & QLowEnergyCharacteristic::Read) { + ComPtr> readOp; + hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp); + Q_ASSERT_SUCCEEDED(hr); + ComPtr readResult; + hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + if (readResult) + charData.value = byteArrayFromGattResult(readResult); + } + + QVector indicateChars; + ComPtr> descriptors; + + ComPtr characteristic2; + hr = characteristic.As(&characteristic2); + Q_ASSERT_SUCCEEDED(hr); + + hr = characteristic2->GetAllDescriptors(&descriptors); Q_ASSERT_SUCCEEDED(hr); - GUID descriptorUuid; - hr = descriptor->get_Uuid(&descriptorUuid); + + + uint descriptorCount; + hr = descriptors->get_Size(&descriptorCount); Q_ASSERT_SUCCEEDED(hr); - descData.uuid = QBluetoothUuid(descriptorUuid); - if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) { - ComPtr> readOp; - hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp); + for (uint j = 0; j < descriptorCount; ++j) { + QLowEnergyServicePrivate::DescData descData; + ComPtr descriptor; + hr = descriptors->GetAt(j, &descriptor); Q_ASSERT_SUCCEEDED(hr); - ComPtr readResult; - hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); + quint16 descHandle; + hr = descriptor->get_AttributeHandle(&descHandle); Q_ASSERT_SUCCEEDED(hr); - GattClientCharacteristicConfigurationDescriptorValue value; - hr = readResult->get_ClientCharacteristicConfigurationDescriptor(&value); + GUID descriptorUuid; + hr = descriptor->get_Uuid(&descriptorUuid); Q_ASSERT_SUCCEEDED(hr); - quint16 result = 0; - bool correct = false; - if (value & GattClientCharacteristicConfigurationDescriptorValue_Indicate) { - result |= GattClientCharacteristicConfigurationDescriptorValue_Indicate; - correct = true; + descData.uuid = QBluetoothUuid(descriptorUuid); + if (descData.uuid == QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)) { + ComPtr> readOp; + hr = characteristic->ReadClientCharacteristicConfigurationDescriptorAsync(&readOp); + Q_ASSERT_SUCCEEDED(hr); + ComPtr readResult; + hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + GattClientCharacteristicConfigurationDescriptorValue value; + hr = readResult->get_ClientCharacteristicConfigurationDescriptor(&value); + Q_ASSERT_SUCCEEDED(hr); + 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()); + indicateChars << charData.uuid; + } else { + ComPtr> readOp; + hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp); + Q_ASSERT_SUCCEEDED(hr); + ComPtr readResult; + hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription) + descData.value = byteArrayFromGattResult(readResult, true); + else + descData.value = byteArrayFromGattResult(readResult); } - if (value & GattClientCharacteristicConfigurationDescriptorValue_Notify) { - result |= GattClientCharacteristicConfigurationDescriptorValue_Notify; - correct = true; - } - if (value == GattClientCharacteristicConfigurationDescriptorValue_None) { - correct = true; - } - if (!correct) - continue; + charData.descriptorList.insert(descHandle, descData); + } - descData.value = QByteArray(2, Qt::Uninitialized); - qToLittleEndian(result, descData.value.data()); - indicateChars << charData.uuid; - } else { - ComPtr> readOp; - hr = descriptor->ReadValueWithCacheModeAsync(BluetoothCacheMode_Uncached, &readOp); - Q_ASSERT_SUCCEEDED(hr); - ComPtr readResult; - hr = QWinRTFunctions::await(readOp, readResult.GetAddressOf()); - Q_ASSERT_SUCCEEDED(hr); - if (descData.uuid == QBluetoothUuid::CharacteristicUserDescription) - descData.value = byteArrayFromGattResult(readResult, true); - else - descData.value = byteArrayFromGattResult(readResult); + mCharacteristicList.insert(handle, charData); + mCharacteristicsCountToBeDiscovered--; + if(mCharacteristicsCountToBeDiscovered == 0){ + emit charListObtained(mService, mCharacteristicList, indicateChars, mStartHandle, mEndHandle); + QThread::currentThread()->quit(); } - charData.descriptorList.insert(descHandle, descData); - } - mCharacteristicList.insert(handle, charData); + return S_OK; + }).Get()); } - emit charListObtained(mService, mCharacteristicList, indicateChars, startHandle, endHandle); - QThread::currentThread()->quit(); } public: QBluetoothUuid mService; ComPtr mDeviceService; QHash mCharacteristicList; + uint mCharacteristicsCountToBeDiscovered; + quint16 mStartHandle = 0; + quint16 mEndHandle = 0; signals: void charListObtained(const QBluetoothUuid &service, QHashget_Size(&serviceCount); Q_ASSERT_SUCCEEDED(hr); - // Windows Phone 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 service; - hr = deviceServices->GetAt(i, &service); - Q_ASSERT_SUCCEEDED(hr); - ComPtr service2; - hr = service.As(&service2); + + // Windows doesn't provide any explicit connect/reconnect. We need to 'start using' the device + // and windows will initiate connection as a cause of that. + if(serviceCount == 0){ + // If we don't have any services discovered yet (for devices not paired), the simplest + // way to initiate connect is to start discovering services. It's not exactly how Qt API + // expects it to be but IMHO doesn't do any harm either. Services will already be discovered + // when coonnection state changes to 'connected'. + ComPtr device3; + hr = mDevice.As(&device3); Q_ASSERT_SUCCEEDED(hr); - ComPtr> characteristics; - hr = service2->GetAllCharacteristics(&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); - return; - } else { - Q_ASSERT_SUCCEEDED(hr); - } - uint characteristicsCount; - hr = characteristics->get_Size(&characteristicsCount); + ComPtr> asyncResult; + hr = device3->GetGattServicesAsync(&asyncResult); Q_ASSERT_SUCCEEDED(hr); - for (uint j = 0; j < characteristicsCount; ++j) { - ComPtr characteristic; - hr = characteristics->GetAt(j, &characteristic); - Q_ASSERT_SUCCEEDED(hr); - ComPtr> op; - GattCharacteristicProperties props; - hr = characteristic->get_CharacteristicProperties(&props); + hr = asyncResult->put_Completed( + Callback>([this, q](IAsyncOperation *op, AsyncStatus) { + setState(QLowEnergyController::ConnectedState); + emit q->connected(); + return S_OK; + }).Get()); + }else{ + // Windows Phone 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 service; + hr = deviceServices->GetAt(i, &service); Q_ASSERT_SUCCEEDED(hr); - if (!(props & GattCharacteristicProperties_Read)) - continue; - hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode::BluetoothCacheMode_Uncached, &op); + ComPtr service2; + hr = service.As(&service2); Q_ASSERT_SUCCEEDED(hr); - ComPtr result; - hr = QWinRTFunctions::await(op, result.GetAddressOf()); - if (hr == E_INVALIDARG) { - // E_INVALIDARG happens when user tries to connect to a device that was paired - // before but is not available. - qCDebug(QT_BT_WINRT) << "Could not obtain characteristic read result that triggers" - "device connection. Is the device reachable?"; - setError(QLowEnergyController::ConnectionError); + ComPtr> characteristics; + hr = service2->GetAllCharacteristics(&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); return; } else { Q_ASSERT_SUCCEEDED(hr); } - ComPtr buffer; - hr = result->get_Value(&buffer); + uint characteristicsCount; + hr = characteristics->get_Size(&characteristicsCount); Q_ASSERT_SUCCEEDED(hr); - if (!buffer) { - qCDebug(QT_BT_WINRT) << "Problem reading value"; - setError(QLowEnergyController::ConnectionError); - setState(QLowEnergyController::UnconnectedState); + for (uint j = 0; j < characteristicsCount; ++j) { + ComPtr characteristic; + hr = characteristics->GetAt(j, &characteristic); + Q_ASSERT_SUCCEEDED(hr); + ComPtr> op; + GattCharacteristicProperties props; + hr = characteristic->get_CharacteristicProperties(&props); + Q_ASSERT_SUCCEEDED(hr); + if (!(props & GattCharacteristicProperties_Read)) + continue; + hr = characteristic->ReadValueWithCacheModeAsync(BluetoothCacheMode::BluetoothCacheMode_Uncached, &op); + Q_ASSERT_SUCCEEDED(hr); + ComPtr result; + hr = QWinRTFunctions::await(op, result.GetAddressOf()); + if (hr == E_INVALIDARG) { + // E_INVALIDARG happens when user tries to connect to a device that was paired + // before but is not available. + qCDebug(QT_BT_WINRT) << "Could not obtain characteristic read result that triggers" + "device connection. Is the device reachable?"; + setError(QLowEnergyController::ConnectionError); + setState(QLowEnergyController::UnconnectedState); + return; + } else { + Q_ASSERT_SUCCEEDED(hr); + } + ComPtr buffer; + hr = result->get_Value(&buffer); + Q_ASSERT_SUCCEEDED(hr); + if (!buffer) { + qCDebug(QT_BT_WINRT) << "Problem reading value"; + setError(QLowEnergyController::ConnectionError); + setState(QLowEnergyController::UnconnectedState); + } + return; } - return; } } } @@ -547,41 +620,54 @@ void QLowEnergyControllerPrivate::discoverServices() Q_Q(QLowEnergyController); qCDebug(QT_BT_WINRT) << "Service discovery initiated"; - ComPtr> deviceServices; - HRESULT hr = mDevice->get_GattServices(&deviceServices); + + ComPtr device3; + HRESULT hr = mDevice.As(&device3); Q_ASSERT_SUCCEEDED(hr); - uint serviceCount; - hr = deviceServices->get_Size(&serviceCount); + ComPtr> asyncResult; + hr = device3->GetGattServicesAsync(&asyncResult); Q_ASSERT_SUCCEEDED(hr); - for (uint i = 0; i < serviceCount; ++i) { - ComPtr deviceService; - hr = deviceServices->GetAt(i, &deviceService); + hr = asyncResult->put_Completed( + Callback>([this, q](IAsyncOperation *op, AsyncStatus) { + + ComPtr> deviceServices; + HRESULT hr = mDevice->get_GattServices(&deviceServices); Q_ASSERT_SUCCEEDED(hr); - GUID guuid; - hr = deviceService->get_Uuid(&guuid); + uint serviceCount; + hr = deviceServices->get_Size(&serviceCount); Q_ASSERT_SUCCEEDED(hr); - const QBluetoothUuid service(guuid); + for (uint i = 0; i < serviceCount; ++i) { + ComPtr deviceService; + hr = deviceServices->GetAt(i, &deviceService); + Q_ASSERT_SUCCEEDED(hr); + GUID guuid; + hr = deviceService->get_Uuid(&guuid); + Q_ASSERT_SUCCEEDED(hr); + const QBluetoothUuid service(guuid); - QSharedPointer pointer; - if (serviceList.contains(service)) { - pointer = serviceList.value(service); - } else { - QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate(); - priv->uuid = service; - priv->setController(this); + QSharedPointer pointer; + if (serviceList.contains(service)) { + pointer = serviceList.value(service); + } else { + QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate(); + priv->uuid = service; + priv->setController(this); - pointer = QSharedPointer(priv); - serviceList.insert(service, pointer); - } - pointer->type |= QLowEnergyService::PrimaryService; + pointer = QSharedPointer(priv); + serviceList.insert(service, pointer); + } + pointer->type |= QLowEnergyService::PrimaryService; - obtainIncludedServices(pointer, deviceService); + obtainIncludedServices(pointer, deviceService); - emit q->serviceDiscovered(service); - } + emit q->serviceDiscovered(service); + } - setState(QLowEnergyController::DiscoveredState); - emit q->discoveryFinished(); + setState(QLowEnergyController::DiscoveredState); + emit q->discoveryFinished(); + + return S_OK; + }).Get()); } void QLowEnergyControllerPrivate::discoverServiceDetails(const QBluetoothUuid &service)