From c9218dbb84581c0e2f2ab6a7a858f64c70e8c58a Mon Sep 17 00:00:00 2001 From: Oliver Wolff <oliver.wolff@qt.io> Date: Mon, 30 Jan 2017 12:26:06 +0100 Subject: [PATCH] winrt: Add bluetooth server support Task-numer: QTBUG-37779 Change-Id: Ieb3ed5dfea7d60b3875cbe97bb26f8060bebcc17 Reviewed-by: Maurice Kalinowski <maurice.kalinowski@qt.io> --- src/bluetooth/bluetooth.pro | 4 +- src/bluetooth/qbluetoothserver.cpp | 2 +- src/bluetooth/qbluetoothserver_p.h | 20 + src/bluetooth/qbluetoothserver_winrt.cpp | 243 +++++++++++ src/bluetooth/qbluetoothserviceinfo_p.h | 22 + src/bluetooth/qbluetoothserviceinfo_winrt.cpp | 378 ++++++++++++++++++ src/bluetooth/qbluetoothsocket_winrt.cpp | 21 +- 7 files changed, 682 insertions(+), 8 deletions(-) create mode 100644 src/bluetooth/qbluetoothserver_winrt.cpp create mode 100644 src/bluetooth/qbluetoothserviceinfo_winrt.cpp diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro index 20f89b8e..b47e234e 100644 --- a/src/bluetooth/bluetooth.pro +++ b/src/bluetooth/bluetooth.pro @@ -200,9 +200,9 @@ qtConfig(bluez):qtHaveModule(dbus) { SOURCES += \ qbluetoothdevicediscoveryagent_winrt.cpp \ qbluetoothlocaldevice_p.cpp \ - qbluetoothserver_p.cpp \ + qbluetoothserver_winrt.cpp \ qbluetoothservicediscoveryagent_winrt.cpp \ - qbluetoothserviceinfo_p.cpp \ + qbluetoothserviceinfo_winrt.cpp \ qbluetoothsocket_winrt.cpp \ qlowenergycontroller_winrt.cpp diff --git a/src/bluetooth/qbluetoothserver.cpp b/src/bluetooth/qbluetoothserver.cpp index a27eb385..41b1d822 100644 --- a/src/bluetooth/qbluetoothserver.cpp +++ b/src/bluetooth/qbluetoothserver.cpp @@ -257,7 +257,7 @@ bool QBluetoothServer::isListening() const { Q_D(const QBluetoothServer); -#ifdef QT_ANDROID_BLUETOOTH +#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) return d->isListening(); #endif diff --git a/src/bluetooth/qbluetoothserver_p.h b/src/bluetooth/qbluetoothserver_p.h index 8797cebd..833c781b 100644 --- a/src/bluetooth/qbluetoothserver_p.h +++ b/src/bluetooth/qbluetoothserver_p.h @@ -69,6 +69,12 @@ QT_FORWARD_DECLARE_CLASS(QSocketNotifier) class ServerAcceptanceThread; #endif +#ifdef QT_WINRT_BLUETOOTH +#include <wrl.h> +// No forward declares because QBluetoothServerPrivate::listener does not work with them +#include <windows.networking.sockets.h> +#endif + QT_BEGIN_NAMESPACE class QBluetoothAddress; @@ -114,7 +120,21 @@ public: bool isListening() const; bool initiateActiveListening(const QBluetoothUuid& uuid, const QString &serviceName); bool deactivateActiveListening(); +#elif defined(QT_WINRT_BLUETOOTH) + EventRegistrationToken connectionToken {-1}; + + mutable QMutex pendingConnectionsMutex; + QVector<Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocket>> pendingConnections; + + Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocketListener> socketListener; + HRESULT handleClientConnection(ABI::Windows::Networking::Sockets::IStreamSocketListener *listener, + ABI::Windows::Networking::Sockets::IStreamSocketListenerConnectionReceivedEventArgs *args); +public: + bool isListening() const; + Microsoft::WRL::ComPtr<ABI::Windows::Networking::Sockets::IStreamSocketListener> listener() { return socketListener; } + bool initiateActiveListening(const QString &serviceName); + bool deactivateActiveListening(); #endif }; diff --git a/src/bluetooth/qbluetoothserver_winrt.cpp b/src/bluetooth/qbluetoothserver_winrt.cpp new file mode 100644 index 00000000..effe4dc9 --- /dev/null +++ b/src/bluetooth/qbluetoothserver_winrt.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "qbluetoothserver.h" +#include "qbluetoothserver_p.h" +#include "qbluetoothsocket.h" +#include "qbluetoothsocket_p.h" + +#include <QtCore/QLoggingCategory> +#include <qfunctions_winrt.h> + +#include <windows.networking.h> +#include <windows.networking.connectivity.h> +#include <windows.networking.sockets.h> + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Devices; +using namespace ABI::Windows::Devices::Enumeration; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Networking; +using namespace ABI::Windows::Networking::Sockets; +using namespace ABI::Windows::Networking::Connectivity; + +typedef ITypedEventHandler<StreamSocketListener *, StreamSocketListenerConnectionReceivedEventArgs *> ClientConnectedHandler; + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT) + +QHash<QBluetoothServerPrivate *, int> __fakeServerPorts; + +QBluetoothServerPrivate::QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol sType) + : maxPendingConnections(1), serverType(sType), m_lastError(QBluetoothServer::NoError), socket(0) +{ + socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol); +} + +QBluetoothServerPrivate::~QBluetoothServerPrivate() +{ + deactivateActiveListening(); + __fakeServerPorts.remove(this); + if (socket) + delete socket; +} + +bool QBluetoothServerPrivate::isListening() const +{ + return __fakeServerPorts.contains(const_cast<QBluetoothServerPrivate *>(this)); +} + +bool QBluetoothServerPrivate::initiateActiveListening(const QString &serviceName) +{ + HStringReference serviceNameRef(reinterpret_cast<LPCWSTR>(serviceName.utf16())); + + ComPtr<IAsyncAction> bindAction; + HRESULT hr = socketListener->BindServiceNameAsync(serviceNameRef.Get(), &bindAction); + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(bindAction); + Q_ASSERT_SUCCEEDED(hr); + return true; +} + +bool QBluetoothServerPrivate::deactivateActiveListening() +{ + if (!isListening()) + return true; + + HRESULT hr; + hr = socketListener->remove_ConnectionReceived(connectionToken); + Q_ASSERT_SUCCEEDED(hr); + return true; +} + +HRESULT QBluetoothServerPrivate::handleClientConnection(IStreamSocketListener *listener, + IStreamSocketListenerConnectionReceivedEventArgs *args) +{ + Q_Q(QBluetoothServer); + if (!socketListener || socketListener.Get() != listener) { + qCDebug(QT_BT_WINRT) << "Accepting connection from wrong listener. We should not be here."; + Q_UNREACHABLE(); + return S_OK; + } + + HRESULT hr; + ComPtr<IStreamSocket> socket; + hr = args->get_Socket(&socket); + Q_ASSERT_SUCCEEDED(hr); + QMutexLocker locker(&pendingConnectionsMutex); + if (pendingConnections.count() < maxPendingConnections) { + qCDebug(QT_BT_WINRT) << "Accepting connection"; + pendingConnections.append(socket); + q->newConnection(); + } else { + qCDebug(QT_BT_WINRT) << "Refusing connection"; + } + + return S_OK; +} + +void QBluetoothServer::close() +{ + Q_D(QBluetoothServer); + + d->deactivateActiveListening(); + __fakeServerPorts.remove(d); +} + +bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port) +{ + Q_UNUSED(address); + Q_D(QBluetoothServer); + if (serverType() != QBluetoothServiceInfo::RfcommProtocol) { + d->m_lastError = UnsupportedProtocolError; + emit error(d->m_lastError); + return false; + } + + if (isListening()) + return false; + + HRESULT hr; + hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocketListener).Get(), + &d->socketListener); + Q_ASSERT_SUCCEEDED(hr); + hr = d->socketListener->add_ConnectionReceived(Callback<ClientConnectedHandler>(d, &QBluetoothServerPrivate::handleClientConnection).Get(), + &d->connectionToken); + Q_ASSERT_SUCCEEDED(hr); + + //We can not register an actual Rfcomm port, because the platform does not allow it + //but we need a way to associate a server with a service + if (port == 0) { //Try to assign a non taken port id + for (int i = 1; ; i++){ + if (__fakeServerPorts.key(i) == 0) { + port = i; + break; + } + } + } + + if (__fakeServerPorts.key(port) == 0) { + __fakeServerPorts[d] = port; + + qCDebug(QT_BT_WINRT) << "Port" << port << "registered"; + } else { + qCWarning(QT_BT_WINRT) << "server with port" << port << "already registered or port invalid"; + d->m_lastError = ServiceAlreadyRegisteredError; + emit error(d->m_lastError); + return false; + } + + return true; +} + +void QBluetoothServer::setMaxPendingConnections(int numConnections) +{ + Q_D(QBluetoothServer); + QMutexLocker locker(&d->pendingConnectionsMutex); + d->maxPendingConnections = numConnections; +} + +bool QBluetoothServer::hasPendingConnections() const +{ + Q_D(const QBluetoothServer); + QMutexLocker locker(&d->pendingConnectionsMutex); + return !d->pendingConnections.isEmpty(); +} + +QBluetoothSocket *QBluetoothServer::nextPendingConnection() +{ + Q_D(QBluetoothServer); + + ComPtr<IStreamSocket> socket = d->pendingConnections.takeFirst(); + + QBluetoothSocket *newSocket = new QBluetoothSocket(); + bool success = newSocket->d_ptr->setSocketDescriptor(qintptr(socket.Get()), d->serverType); + if (!success) { + delete newSocket; + newSocket = 0; + } + + return newSocket; +} + +QBluetoothAddress QBluetoothServer::serverAddress() const +{ + return QBluetoothAddress(); +} + +quint16 QBluetoothServer::serverPort() const +{ + //We return the fake port + Q_D(const QBluetoothServer); + return __fakeServerPorts.value((QBluetoothServerPrivate*)d, 0); +} + +void QBluetoothServer::setSecurityFlags(QBluetooth::SecurityFlags security) +{ + Q_UNUSED(security); +} + +QBluetooth::SecurityFlags QBluetoothServer::securityFlags() const +{ + return QBluetooth::NoSecurity; +} + +QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothserviceinfo_p.h b/src/bluetooth/qbluetoothserviceinfo_p.h index f1f3b669..2e223a7b 100644 --- a/src/bluetooth/qbluetoothserviceinfo_p.h +++ b/src/bluetooth/qbluetoothserviceinfo_p.h @@ -62,6 +62,22 @@ class OrgBluezServiceInterface; class OrgBluezProfileManager1Interface; +#ifdef QT_WINRT_BLUETOOTH +#include <wrl.h> + +namespace ABI { + namespace Windows { + namespace Devices { + namespace Bluetooth { + namespace Rfcomm { + struct IRfcommServiceProvider; + } + } + } + } +} +#endif + QT_BEGIN_NAMESPACE class QBluetoothServiceInfo; @@ -98,6 +114,12 @@ private: QString profilePath; #endif +#ifdef QT_WINRT_BLUETOOTH + Microsoft::WRL::ComPtr<ABI::Windows::Devices::Bluetooth::Rfcomm::IRfcommServiceProvider> serviceProvider; + + bool writeSdpAttributes(); +#endif + mutable bool registered; }; diff --git a/src/bluetooth/qbluetoothserviceinfo_winrt.cpp b/src/bluetooth/qbluetoothserviceinfo_winrt.cpp new file mode 100644 index 00000000..77376dd9 --- /dev/null +++ b/src/bluetooth/qbluetoothserviceinfo_winrt.cpp @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "qbluetoothserviceinfo.h" +#include "qbluetoothserviceinfo_p.h" +#include "qbluetoothserver_p.h" + +#include <QtCore/QLoggingCategory> +#include <qfunctions_winrt.h> + +#include <wrl.h> +#include <windows.devices.bluetooth.h> +#include <windows.devices.bluetooth.rfcomm.h> +#include <windows.foundation.h> +#include <windows.networking.sockets.h> +#include <windows.storage.streams.h> + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Devices::Bluetooth; +using namespace ABI::Windows::Devices::Bluetooth::Rfcomm; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Networking::Sockets; +using namespace ABI::Windows::Storage::Streams; + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT) + +#define TYPE_UINT8 8 +#define TYPE_UINT16 9 +#define TYPE_UINT32 10 +#define TYPE_SHORT_UUID 25 +#define TYPE_LONG_UUID 28 +#define TYPE_STRING 37 +#define TYPE_SEQUENCE 53 + +extern QHash<QBluetoothServerPrivate *, int> __fakeServerPorts; + +static ComPtr<IBuffer> bufferFromAttribute(const QVariant &attribute) +{ + ComPtr<IDataWriter> writer; + HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataWriter).Get(), + &writer); + Q_ASSERT_SUCCEEDED(hr); + + switch (int(attribute.type())) { + case QMetaType::Void: + qCWarning(QT_BT_WINRT) << "Don't know how to register QMetaType::Void"; + return nullptr; + case QMetaType::UChar: + qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering attribute of type QMetaType::UChar"; + hr = writer->WriteByte(TYPE_UINT8); + Q_ASSERT_SUCCEEDED(hr); + hr = writer->WriteByte(attribute.value<quint8>()); + Q_ASSERT_SUCCEEDED(hr); + break; + case QMetaType::UShort: + qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering attribute of type QMetaType::UShort"; + hr = writer->WriteByte(TYPE_UINT16); + Q_ASSERT_SUCCEEDED(hr); + hr = writer->WriteUInt16(attribute.value<quint16>()); + Q_ASSERT_SUCCEEDED(hr); + break; + case QMetaType::UInt: + qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering attribute of type QMetaType::UInt"; + hr = writer->WriteByte(TYPE_UINT32); + Q_ASSERT_SUCCEEDED(hr); + hr = writer->WriteByte(attribute.value<quint32>()); + Q_ASSERT_SUCCEEDED(hr); + break; + case QMetaType::Char: + qCWarning(QT_BT_WINRT) << "Don't know how to register QMetaType::Char"; + return nullptr; + break; + case QMetaType::Short: + qCWarning(QT_BT_WINRT) << "Don't know how to register QMetaType::Short"; + return nullptr; + break; + case QMetaType::Int: + qCWarning(QT_BT_WINRT) << "Don't know how to register QMetaType::Int"; + return nullptr; + break; + case QMetaType::QString: { + qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering attribute of type QMetaType::QString"; + hr = writer->WriteByte(TYPE_STRING); + Q_ASSERT_SUCCEEDED(hr); + const QString stringValue = attribute.value<QString>(); + hr = writer->WriteByte(stringValue.length()); + Q_ASSERT_SUCCEEDED(hr); + HStringReference stringRef(reinterpret_cast<LPCWSTR>(stringValue.utf16())); + quint32 bytesWritten; + hr = writer->WriteString(stringRef.Get(), &bytesWritten); + if (bytesWritten != stringValue.length()) { + qCWarning(QT_BT_WINRT) << "Did not write full value to buffer"; + return nullptr; + } + Q_ASSERT_SUCCEEDED(hr); + break; + } + case QMetaType::Bool: + qCWarning(QT_BT_WINRT) << "Don't know how to register QMetaType::Bool"; + return nullptr; + break; + case QMetaType::QUrl: + qCWarning(QT_BT_WINRT) << "Don't know how to register QMetaType::QUrl"; + return nullptr; + break; + case QVariant::UserType: + if (attribute.userType() == qMetaTypeId<QBluetoothUuid>()) { + QBluetoothUuid uuid = attribute.value<QBluetoothUuid>(); + const int minimumSize = uuid.minimumSize(); + switch (uuid.minimumSize()) { + case 0: + qCWarning(QT_BT_WINRT) << "Don't know how to register Uuid of length 0"; + return nullptr; + break; + case 2: + qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering Uuid attribute with length 2" << uuid; + hr = writer->WriteByte(TYPE_SHORT_UUID); + Q_ASSERT_SUCCEEDED(hr); + hr = writer->WriteUInt16(uuid.toUInt16()); + Q_ASSERT_SUCCEEDED(hr); + break; + case 4: + qCWarning(QT_BT_WINRT) << "Don't know how to register Uuid of length 4"; + return nullptr; + break; + case 16: + qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering Uuid attribute with length 16"; + hr = writer->WriteByte(TYPE_LONG_UUID); + Q_ASSERT_SUCCEEDED(hr); + hr = writer->WriteGuid(uuid); + Q_ASSERT_SUCCEEDED(hr); + break; + default: + qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registering Uuid attribute"; + hr = writer->WriteByte(TYPE_LONG_UUID); + Q_ASSERT_SUCCEEDED(hr); + hr = writer->WriteGuid(uuid); + Q_ASSERT_SUCCEEDED(hr); + break; + } + } else if (attribute.userType() == qMetaTypeId<QBluetoothServiceInfo::Sequence>()) { + qCDebug(QT_BT_WINRT) << "Registering sequence attribute"; + hr = writer->WriteByte(TYPE_SEQUENCE); + Q_ASSERT_SUCCEEDED(hr); + const QBluetoothServiceInfo::Sequence *sequence = + static_cast<const QBluetoothServiceInfo::Sequence *>(attribute.data()); + ComPtr<IDataWriter> tmpWriter; + HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataWriter).Get(), + &tmpWriter); + Q_ASSERT_SUCCEEDED(hr); + foreach (const QVariant &v, *sequence) { + ComPtr<IBuffer> tmpBuffer = bufferFromAttribute(v); + if (!tmpBuffer) { + qCWarning(QT_BT_WINRT) << "Could not create buffer from attribute in sequence"; + return nullptr; + } + quint32 l; + hr = tmpBuffer->get_Length(&l); + Q_ASSERT_SUCCEEDED(hr); + hr = tmpWriter->WriteBuffer(tmpBuffer.Get()); + Q_ASSERT_SUCCEEDED(hr); + } + ComPtr<IBuffer> tmpBuffer; + hr = tmpWriter->DetachBuffer(&tmpBuffer); + Q_ASSERT_SUCCEEDED(hr); + // write sequence length + quint32 length; + tmpBuffer->get_Length(&length); + Q_ASSERT_SUCCEEDED(hr); + hr = writer->WriteByte(length + 1); + Q_ASSERT_SUCCEEDED(hr); + // write sequence data + hr = writer->WriteBuffer(tmpBuffer.Get()); + Q_ASSERT_SUCCEEDED(hr); + qCDebug(QT_BT_WINRT) << Q_FUNC_INFO << "Registered sequence attribute with length" << length; + } else if (attribute.userType() == qMetaTypeId<QBluetoothServiceInfo::Alternative>()) { + qCWarning(QT_BT_WINRT) << "Don't know how to register user type Alternative"; + return false; + } + break; + default: + qCWarning(QT_BT_WINRT) << "Unknown variant type", attribute.userType(); + return nullptr; + } + ComPtr<IBuffer> buffer; + hr = writer->DetachBuffer(&buffer); + Q_ASSERT_SUCCEEDED(hr); + return buffer; +} + +QBluetoothServiceInfoPrivate::QBluetoothServiceInfoPrivate() + : registered(false) +{ +} + +QBluetoothServiceInfoPrivate::~QBluetoothServiceInfoPrivate() +{ +} + +bool QBluetoothServiceInfoPrivate::isRegistered() const +{ + return registered; +} + +bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &localAdapter) +{ + Q_UNUSED(localAdapter); + if (registered) + return false; + + if (protocolDescriptor(QBluetoothUuid::Rfcomm).isEmpty()) { + qCWarning(QT_BT_WINRT) << Q_FUNC_INFO << "Only RFCOMM services can be registered on WinRT"; + return false; + } + + QBluetoothServerPrivate *sPriv = __fakeServerPorts.key(serverChannel()); + if (!sPriv) + return false; + + HRESULT hr; + QBluetoothUuid uuid = attributes.value(QBluetoothServiceInfo::ServiceId).value<QBluetoothUuid>(); + ComPtr<IRfcommServiceIdStatics> serviceIdStatics; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_Rfcomm_RfcommServiceId).Get(), + IID_PPV_ARGS(&serviceIdStatics)); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IRfcommServiceId> serviceId; + hr = serviceIdStatics->FromUuid(uuid, &serviceId); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IRfcommServiceProviderStatics> providerStatics; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Bluetooth_Rfcomm_RfcommServiceProvider).Get(), + IID_PPV_ARGS(&providerStatics)); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IAsyncOperation<RfcommServiceProvider *>> op; + hr = providerStatics->CreateAsync(serviceId.Get(), &op); + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(op, serviceProvider.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IStreamSocketListener> listener = sPriv->listener(); + if (!listener) { + qCWarning(QT_BT_WINRT) << Q_FUNC_INFO << "Could not obtain listener from server."; + return false; + } + + + HString serviceIdHString; + serviceId->AsString(serviceIdHString.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + const QString serviceIdString = QString::fromWCharArray(WindowsGetStringRawBuffer(serviceIdHString.Get(), nullptr)); + + //tell the server what service name our listener should have + //and start the real listener + bool result = sPriv->initiateActiveListening(serviceIdString); + if (!result) { + return false; + } + + result = writeSdpAttributes(); + if (!result) { + return false; + } + + hr = serviceProvider->StartAdvertising(listener.Get()); + if (FAILED(hr)) { + qCWarning(QT_BT_WINRT) << Q_FUNC_INFO << "Could not start advertising. Check your SDP data."; + return false; + } + + registered = true; + return true; +} + +bool QBluetoothServiceInfoPrivate::unregisterService() +{ + if (!registered) + return false; + + QBluetoothServerPrivate *sPriv = __fakeServerPorts.key(serverChannel()); + if (!sPriv) { + //QBluetoothServer::close() was called without prior call to unregisterService(). + //Now it is unregistered anyway. + registered = false; + return true; + } + + bool result = sPriv->deactivateActiveListening(); + if (!result) + return false; + + HRESULT hr; + hr = serviceProvider->StopAdvertising(); + Q_ASSERT_SUCCEEDED(hr); + + registered = false; + return true; +} + +bool QBluetoothServiceInfoPrivate::writeSdpAttributes() +{ + if (!serviceProvider) + return false; + + ComPtr<IDataWriter> writer; + HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_DataWriter).Get(), + &writer); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IMap<UINT32, IBuffer *>> rawAttributes; + hr = serviceProvider->get_SdpRawAttributes(&rawAttributes); + Q_ASSERT_SUCCEEDED(hr); + for (quint16 key : attributes.keys()) { + // The SDP Class Id List and RFCOMM and L2CAP protocol descriptors are automatically + // generated by the RfcommServiceProvider. Do not specify it in the SDP raw attribute map. + if (key == QBluetoothServiceInfo::ServiceClassIds + || key == QBluetoothServiceInfo::ProtocolDescriptorList) + continue; + const QVariant attribute = attributes.value(key); + HRESULT hr; + ComPtr<IBuffer> buffer = bufferFromAttribute(attribute); + if (!buffer) { + qCWarning(QT_BT_WINRT) << "Could not create buffer from attribute with id:" << key; + return false; + } + + hr = writer->WriteBuffer(buffer.Get()); + Q_ASSERT_SUCCEEDED(hr); + + hr = writer->DetachBuffer(&buffer); + Q_ASSERT_SUCCEEDED(hr); + + boolean replaced; + hr = rawAttributes->Insert(key, buffer.Get(), &replaced); + Q_ASSERT_SUCCEEDED(hr); + Q_ASSERT(!replaced); + } + return true; +} + +QT_END_NAMESPACE diff --git a/src/bluetooth/qbluetoothsocket_winrt.cpp b/src/bluetooth/qbluetoothsocket_winrt.cpp index 98847b18..855af16d 100644 --- a/src/bluetooth/qbluetoothsocket_winrt.cpp +++ b/src/bluetooth/qbluetoothsocket_winrt.cpp @@ -533,11 +533,22 @@ void QBluetoothSocketPrivate::close() bool QBluetoothSocketPrivate::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType, QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode) { - Q_UNUSED(socketDescriptor); - Q_UNUSED(socketType) - Q_UNUSED(socketState); - Q_UNUSED(openMode); - return false; + Q_Q(QBluetoothSocket); + if (socketType != QBluetoothServiceInfo::RfcommProtocol) + return false; + + m_socketObject = nullptr; + socket = -1; + + m_socketObject = reinterpret_cast<IStreamSocket *>(qintptr(socketDescriptor)); + if (!m_socketObject) + return false; + socket = qintptr(m_socketObject.Get()); + m_worker->setSocket(m_socketObject); + if (socketState == QBluetoothSocket::ConnectedState) + m_worker->startReading(); + q->setOpenMode(openMode); + return true; } qint64 QBluetoothSocketPrivate::bytesAvailable() const -- GitLab