qbluetoothsocket_winrt: Use recommended service connection approach if possible

MSDN documentation states that the recommended way of RFCOMM service
connection is via connectionHostName and connectionServiceName (see
https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.rfcomm.rfcommdeviceservice).
So whenever possible, we should use this information from the native
device service when connecting directly to the discovered service.

As QBluetoothServiceInfo is basically just a wrapper for the attributes
map this information is stored in its private pendant and extracted when
a connection attempt is being made.

Task-number: QTBUG-62520
Change-Id: I95be5df89a722531393b45fd136d37f302393ca8
Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
This commit is contained in:
Oliver Wolff 2019-05-09 15:17:32 +02:00
parent ea85065cbb
commit 672ebe7690
4 changed files with 80 additions and 17 deletions

View File

@ -52,6 +52,7 @@
#include <windows.devices.enumeration.h>
#include <windows.devices.bluetooth.h>
#include <windows.foundation.collections.h>
#include <windows.networking.h>
#include <windows.storage.streams.h>
#include <wrl.h>
@ -208,6 +209,14 @@ void QWinRTBluetoothServiceDiscoveryWorker::processServiceSearchResult(quint64 a
hr = service->get_ConnectionServiceName(name.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
const QString serviceName = QString::fromWCharArray(WindowsGetStringRawBuffer(name.Get(), nullptr));
ComPtr<ABI::Windows::Networking::IHostName> host;
hr = service->get_ConnectionHostName(host.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
HString hostName;
hr = host->get_RawName(hostName.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
const QString qHostName = QString::fromWCharArray(WindowsGetStringRawBuffer(hostName.Get(),
nullptr));
ComPtr<IRfcommServiceId> id;
hr = service->get_ServiceId(&id);
Q_ASSERT_SUCCEEDED(hr);
@ -217,6 +226,8 @@ void QWinRTBluetoothServiceDiscoveryWorker::processServiceSearchResult(quint64 a
Q_ASSERT_SUCCEEDED(hr);
QBluetoothServiceInfo info;
info.setAttribute(0xBEEF, QVariant(qHostName));
info.setAttribute(0xBEF0, QVariant(serviceName));
info.setServiceName(serviceName);
info.setServiceUuid(uuid);
ComPtr<IAsyncOperation<IMapView<UINT32, IBuffer *> *>> op;

View File

@ -640,6 +640,13 @@ void QBluetoothSocket::serviceDiscovered(const QBluetoothServiceInfo &service)
connectToService(service, d->openMode);
d->discoveryAgent->deleteLater();
d->discoveryAgent = nullptr;
#ifdef QT_WINRT_BLUETOOTH
} else if (!service.attribute(0xBEEF).isNull()
&& !service.attribute(0xBEF0).isNull()) {
connectToService(service, d->openMode);
d->discoveryAgent->deleteLater();
d->discoveryAgent = nullptr;
#endif
} else {
qCDebug(QT_BT) << "Could not find port/psm for potential remote service";
}

View File

@ -359,10 +359,11 @@ bool QBluetoothSocketPrivateWinRT::ensureNativeSocket(QBluetoothServiceInfo::Pro
return true;
}
void QBluetoothSocketPrivateWinRT::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
void QBluetoothSocketPrivateWinRT::connectToService(Microsoft::WRL::ComPtr<IHostName> hostName,
const QString &serviceName,
QIODevice::OpenMode openMode)
{
Q_Q(QBluetoothSocket);
Q_UNUSED(openMode);
if (socket == -1 && !ensureNativeSocket(socketType)) {
errorString = QBluetoothSocket::tr("Unknown socket error");
@ -370,20 +371,9 @@ void QBluetoothSocketPrivateWinRT::connectToServiceHelper(const QBluetoothAddres
return;
}
const QString addressString = address.toString();
HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(addressString.utf16()));
ComPtr<IHostNameFactory> hostNameFactory;
HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
&hostNameFactory);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IHostName> remoteHost;
hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
RETURN_VOID_IF_FAILED("QBluetoothSocketPrivateWinRT::connectToService: Could not create hostname.");
HStringReference serviceNameReference(reinterpret_cast<LPCWSTR>(serviceName.utf16()));
const QString portString = QString::number(port);
HStringReference portReference(reinterpret_cast<LPCWSTR>(portString.utf16()));
hr = m_socketObject->ConnectAsync(remoteHost.Get(), portReference.Get(), &m_connectOp);
HRESULT hr = m_socketObject->ConnectAsync(hostName.Get(), serviceNameReference.Get(), &m_connectOp);
if (hr == E_ACCESSDENIED) {
qErrnoWarning(hr, "QBluetoothSocketPrivateWinRT::connectToService: Unable to connect to bluetooth socket."
"Please check your manifest capabilities.");
@ -404,6 +394,29 @@ void QBluetoothSocketPrivateWinRT::connectToServiceHelper(const QBluetoothAddres
Q_ASSERT_SUCCEEDED(hr);
}
void QBluetoothSocketPrivateWinRT::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
{
Q_Q(QBluetoothSocket);
if (socket == -1 && !ensureNativeSocket(socketType)) {
errorString = QBluetoothSocket::tr("Unknown socket error");
q->setSocketError(QBluetoothSocket::UnknownSocketError);
return;
}
const QString addressString = address.toString();
HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(addressString.utf16()));
ComPtr<IHostNameFactory> hostNameFactory;
HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
&hostNameFactory);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IHostName> remoteHost;
hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
RETURN_VOID_IF_FAILED("QBluetoothSocketPrivateWinRT::connectToService: Could not create hostname.");
const QString portString = QString::number(port);
connectToService(remoteHost, portString, openMode);
}
void QBluetoothSocketPrivateWinRT::connectToService(
const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
{
@ -425,6 +438,8 @@ void QBluetoothSocketPrivateWinRT::connectToService(
return;
}
const QString connectionHostName = service.attribute(0xBEEF).toString();
const QString connectionServiceName = service.attribute(0xBEF0).toString();
if (service.protocolServiceMultiplexer() > 0) {
Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::L2capProtocol);
@ -434,7 +449,22 @@ void QBluetoothSocketPrivateWinRT::connectToService(
return;
}
connectToServiceHelper(service.device().address(), service.protocolServiceMultiplexer(), openMode);
} else if (service.serverChannel() > 0) {
} else if (!connectionHostName.isEmpty() && !connectionServiceName.isEmpty()) {
Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::RfcommProtocol);
if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) {
errorString = QBluetoothSocket::tr("Unknown socket error");
q->setSocketError(QBluetoothSocket::UnknownSocketError);
return;
}
HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(connectionHostName.utf16()));
ComPtr<IHostNameFactory> hostNameFactory;
HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
&hostNameFactory);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IHostName> remoteHost;
hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
connectToService(remoteHost, connectionServiceName, openMode);
} else if (service.serverChannel() > 0) {
Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::RfcommProtocol);
if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) {
@ -443,7 +473,7 @@ void QBluetoothSocketPrivateWinRT::connectToService(
return;
}
connectToServiceHelper(service.device().address(), service.serverChannel(), openMode);
} else {
} else {
// try doing service discovery to see if we can find the socket
if (service.serviceUuid().isNull()
&& !service.serviceClassUuids().contains(QBluetoothUuid::SerialPort)) {

View File

@ -57,6 +57,19 @@
QT_FORWARD_DECLARE_CLASS(SocketWorker)
namespace ABI {
namespace Windows {
namespace Networking {
struct IHostName;
}
}
}
namespace Microsoft {
namespace WRL {
template <typename T> class ComPtr;
}
}
QT_BEGIN_NAMESPACE
class QBluetoothSocketPrivateWinRT final: public QBluetoothSocketBasePrivate
@ -125,6 +138,8 @@ private slots:
void handleError(QBluetoothSocket::SocketError error);
private:
void connectToService(Microsoft::WRL::ComPtr<ABI::Windows::Networking::IHostName> hostName,
const QString &serviceName, QIODevice::OpenMode openMode);
HRESULT handleConnectOpFinished(ABI::Windows::Foundation::IAsyncAction *action,
ABI::Windows::Foundation::AsyncStatus status);