diff options
author | Oliver Wolff <oliver.wolff@qt.io> | 2017-01-30 12:26:06 +0100 |
---|---|---|
committer | Oliver Wolff <oliver.wolff@qt.io> | 2017-01-31 08:43:34 +0000 |
commit | c9218dbb84581c0e2f2ab6a7a858f64c70e8c58a (patch) | |
tree | 57a28e43579bb8e07a465eb1aaff02d95359368f /src/bluetooth/qbluetoothserviceinfo_winrt.cpp | |
parent | 4abc1ebedc13b7ec9a3ea8537dc08d0268c0022b (diff) |
winrt: Add bluetooth server support
Task-numer: QTBUG-37779
Change-Id: Ieb3ed5dfea7d60b3875cbe97bb26f8060bebcc17
Reviewed-by: Maurice Kalinowski <maurice.kalinowski@qt.io>
Diffstat (limited to 'src/bluetooth/qbluetoothserviceinfo_winrt.cpp')
-rw-r--r-- | src/bluetooth/qbluetoothserviceinfo_winrt.cpp | 378 |
1 files changed, 378 insertions, 0 deletions
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 |