From d67ca376b2111717de88596888f315fba05ef9c3 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Tue, 7 May 2019 16:04:59 +0200 Subject: BlueZ: Fix leaking client socket when running QLEController Peripheral mode In fact there are two socket leaks. The first is the socket for the incoming l2cp connection from the central device and the second one is allocated in the ctor of QBluetoothSocket. When QBluetoothSocket::setSocketDescriptor is called the previously ctor allocated socket was simply ignorred. This patch closes both socket. Fixes: QTBUG-75278 Change-Id: Ia483e3c2a04bec3a53ddf744c22b794941edf848 Reviewed-by: Timur Pocheptsov Reviewed-by: Oliver Wolff --- src/bluetooth/qbluetoothsocket_bluez.cpp | 3 +++ src/bluetooth/qlowenergycontroller_bluez.cpp | 8 ++++++++ 2 files changed, 11 insertions(+) (limited to 'src') diff --git a/src/bluetooth/qbluetoothsocket_bluez.cpp b/src/bluetooth/qbluetoothsocket_bluez.cpp index bbc32a90..25f07bb3 100644 --- a/src/bluetooth/qbluetoothsocket_bluez.cpp +++ b/src/bluetooth/qbluetoothsocket_bluez.cpp @@ -674,6 +674,9 @@ bool QBluetoothSocketPrivateBluez::setSocketDescriptor(int socketDescriptor, QBl connectWriteNotifier = nullptr; socketType = socketType_; + if (socket != -1) + QT_CLOSE(socket); + socket = socketDescriptor; // ensure that O_NONBLOCK is set on new connections. diff --git a/src/bluetooth/qlowenergycontroller_bluez.cpp b/src/bluetooth/qlowenergycontroller_bluez.cpp index 65f4e0c2..dfa21004 100644 --- a/src/bluetooth/qlowenergycontroller_bluez.cpp +++ b/src/bluetooth/qlowenergycontroller_bluez.cpp @@ -3115,6 +3115,14 @@ void QLowEnergyControllerPrivateBluez::handleConnectionRequest() if (connectionHandle == 0) qCWarning(QT_BT_BLUEZ) << "Received client connection, but no connection complete event"; + if (l2cpSocket) { + disconnect(l2cpSocket); + if (l2cpSocket->isOpen()) + l2cpSocket->close(); + + l2cpSocket->deleteLater(); + l2cpSocket = nullptr; + } closeServerSocket(); QBluetoothSocketPrivateBluez *rawSocketPrivate = new QBluetoothSocketPrivateBluez(); -- cgit v1.2.3 From 672ebe7690455c6ee13f96ad05d030183a7e57f8 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Thu, 9 May 2019 15:17:32 +0200 Subject: 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 --- .../qbluetoothservicediscoveryagent_winrt.cpp | 11 ++++ src/bluetooth/qbluetoothsocket.cpp | 7 +++ src/bluetooth/qbluetoothsocket_winrt.cpp | 64 ++++++++++++++++------ src/bluetooth/qbluetoothsocket_winrt_p.h | 15 +++++ 4 files changed, 80 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp index 9f82a202..3b99c1c4 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -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 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 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 *>> op; diff --git a/src/bluetooth/qbluetoothsocket.cpp b/src/bluetooth/qbluetoothsocket.cpp index a18b8ed4..db7c8be4 100644 --- a/src/bluetooth/qbluetoothsocket.cpp +++ b/src/bluetooth/qbluetoothsocket.cpp @@ -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"; } diff --git a/src/bluetooth/qbluetoothsocket_winrt.cpp b/src/bluetooth/qbluetoothsocket_winrt.cpp index a4af8f72..72d72040 100644 --- a/src/bluetooth/qbluetoothsocket_winrt.cpp +++ b/src/bluetooth/qbluetoothsocket_winrt.cpp @@ -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 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(addressString.utf16())); - ComPtr hostNameFactory; - HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(), - &hostNameFactory); - Q_ASSERT_SUCCEEDED(hr); - ComPtr remoteHost; - hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost); - RETURN_VOID_IF_FAILED("QBluetoothSocketPrivateWinRT::connectToService: Could not create hostname."); - - const QString portString = QString::number(port); - HStringReference portReference(reinterpret_cast(portString.utf16())); + HStringReference serviceNameReference(reinterpret_cast(serviceName.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(addressString.utf16())); + ComPtr hostNameFactory; + HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(), + &hostNameFactory); + Q_ASSERT_SUCCEEDED(hr); + ComPtr 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(connectionHostName.utf16())); + ComPtr hostNameFactory; + HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(), + &hostNameFactory); + Q_ASSERT_SUCCEEDED(hr); + ComPtr 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)) { diff --git a/src/bluetooth/qbluetoothsocket_winrt_p.h b/src/bluetooth/qbluetoothsocket_winrt_p.h index 40e87f01..de8b7d67 100644 --- a/src/bluetooth/qbluetoothsocket_winrt_p.h +++ b/src/bluetooth/qbluetoothsocket_winrt_p.h @@ -57,6 +57,19 @@ QT_FORWARD_DECLARE_CLASS(SocketWorker) +namespace ABI { + namespace Windows { + namespace Networking { + struct IHostName; + } + } +} + +namespace Microsoft { + namespace WRL { + template 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 hostName, + const QString &serviceName, QIODevice::OpenMode openMode); HRESULT handleConnectOpFinished(ABI::Windows::Foundation::IAsyncAction *action, ABI::Windows::Foundation::AsyncStatus status); -- cgit v1.2.3 From b5ec1e15f360cd60386488588dd4796169082355 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Fri, 10 May 2019 11:17:21 +0200 Subject: winrt: Make sure that ProtocolDescriptorList is always set for services For some devices the attribute map does not have a protocol descriptor set which throws off Qt's logic. As Windows can only discover RFCOMM services, we can just add that protocol to the service ourselves if it is not found automatically. Task-number: QTBUG-62520 Change-Id: I6ce3948892699049b678b026840d346879b98269 Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src') diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp index 3b99c1c4..f1476758 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_winrt.cpp @@ -336,6 +336,17 @@ void QWinRTBluetoothServiceDiscoveryWorker::processServiceSearchResult(quint64 a } hr = iterator->MoveNext(¤t); } + // Windows is only able to discover Rfcomm services but the according protocolDescriptor is + // not always set in the raw attribute map. If we encounter a service like that we should + // fill the protocol descriptor ourselves. + if (info.protocolDescriptor(QBluetoothUuid::Rfcomm).isEmpty()) { + QBluetoothServiceInfo::Sequence protocolDescriptorList; + QBluetoothServiceInfo::Sequence protocol; + protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm)) + << QVariant::fromValue(0); + protocolDescriptorList.append(QVariant::fromValue(protocol)); + info.setAttribute(QBluetoothServiceInfo::ProtocolDescriptorList, protocolDescriptorList); + } emit serviceFound(address, info); } emit scanFinished(address); -- cgit v1.2.3