summaryrefslogtreecommitdiffstats
path: root/src/bluetooth/qbluetoothsocket_winrt.cpp
diff options
context:
space:
mode:
authorOliver Wolff <oliver.wolff@qt.io>2017-01-18 15:18:48 +0100
committerOliver Wolff <oliver.wolff@qt.io>2017-01-20 07:26:36 +0000
commit8a0d14f4933bcd933e1258731472479ec33ade6a (patch)
treeb88d0cc5397e52224f7bbdd7f51642ea206f393e /src/bluetooth/qbluetoothsocket_winrt.cpp
parent9061ea41bb0c88be0af4252171d645bcc7882046 (diff)
winrt: Add bluetooth socket support
Task-number: QTBUG-37779 Change-Id: I7fb49a6870768da956793b0d0681c371da939df9 Reviewed-by: Maurice Kalinowski <maurice.kalinowski@qt.io> Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
Diffstat (limited to 'src/bluetooth/qbluetoothsocket_winrt.cpp')
-rw-r--r--src/bluetooth/qbluetoothsocket_winrt.cpp644
1 files changed, 644 insertions, 0 deletions
diff --git a/src/bluetooth/qbluetoothsocket_winrt.cpp b/src/bluetooth/qbluetoothsocket_winrt.cpp
new file mode 100644
index 00000000..b8a57adb
--- /dev/null
+++ b/src/bluetooth/qbluetoothsocket_winrt.cpp
@@ -0,0 +1,644 @@
+/****************************************************************************
+**
+** 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 "qbluetoothsocket.h"
+#include "qbluetoothsocket_p.h"
+
+#include <qfunctions_winrt.h>
+
+#include <private/qeventdispatcher_winrt_p.h>
+
+#include <QtBluetooth/QBluetoothLocalDevice>
+#include <QtCore/qloggingcategory.h>
+
+#include <robuffer.h>
+#include <windows.devices.bluetooth.h>
+#include <windows.networking.sockets.h>
+#include <windows.storage.streams.h>
+#include <wrl.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;
+using namespace ABI::Windows::Networking::Sockets;
+using namespace ABI::Windows::Storage::Streams;
+
+typedef IAsyncOperationWithProgressCompletedHandler<IBuffer *, UINT32> SocketReadCompletedHandler;
+typedef IAsyncOperationWithProgress<IBuffer *, UINT32> IAsyncBufferOperation;
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
+
+struct SocketGlobal
+{
+ SocketGlobal()
+ {
+ HRESULT hr;
+ hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
+ &bufferFactory);
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+
+ ComPtr<IBufferFactory> bufferFactory;
+};
+Q_GLOBAL_STATIC(SocketGlobal, g)
+
+#define READ_BUFFER_SIZE 65536
+
+static inline QString qt_QStringFromHString(const HString &string)
+{
+ UINT32 length;
+ PCWSTR rawString = string.GetRawBuffer(&length);
+ return QString::fromWCharArray(rawString, length);
+}
+
+static qint64 writeIOStream(ComPtr<IOutputStream> stream, const char *data, qint64 len)
+{
+ ComPtr<IBuffer> buffer;
+ HRESULT hr = g->bufferFactory->Create(len, &buffer);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = buffer->put_Length(len);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
+ hr = buffer.As(&byteArrayAccess);
+ Q_ASSERT_SUCCEEDED(hr);
+ byte *bytes;
+ hr = byteArrayAccess->Buffer(&bytes);
+ Q_ASSERT_SUCCEEDED(hr);
+ memcpy(bytes, data, len);
+ ComPtr<IAsyncOperationWithProgress<UINT32, UINT32>> op;
+ hr = stream->WriteAsync(buffer.Get(), &op);
+ RETURN_IF_FAILED("Failed to write to stream", return -1);
+ UINT32 bytesWritten;
+ hr = QWinRTFunctions::await(op, &bytesWritten);
+ RETURN_IF_FAILED("Failed to write to stream", return -1);
+ return bytesWritten;
+}
+
+class SocketWorker : public QObject
+{
+ Q_OBJECT
+public:
+ SocketWorker()
+ {
+ }
+
+ ~SocketWorker()
+ {
+ if (Q_UNLIKELY(m_initialReadOp)) {
+ ComPtr<IAsyncInfo> info;
+ HRESULT hr = m_initialReadOp.As(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ if (info) {
+ hr = info->Cancel();
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = info->Close();
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+ }
+
+ if (m_readOp) {
+ ComPtr<IAsyncInfo> info;
+ HRESULT hr = m_readOp.As(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ if (info) {
+ hr = info->Cancel();
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = info->Close();
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+ }
+ }
+
+signals:
+ void newDataReceived(const QVector<QByteArray> &data);
+ void socketErrorOccured(QBluetoothSocket::SocketError error);
+
+public slots:
+ Q_INVOKABLE void notifyAboutNewData()
+ {
+ QMutexLocker locker(&m_mutex);
+ const QVector<QByteArray> newData = std::move(m_pendingData);
+ m_pendingData.clear();
+ emit newDataReceived(newData);
+ }
+
+public:
+ void startReading()
+ {
+ ComPtr<IBuffer> buffer;
+ HRESULT hr = g->bufferFactory->Create(READ_BUFFER_SIZE, &buffer);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IInputStream> stream;
+ hr = m_socket->get_InputStream(&stream);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = stream->ReadAsync(buffer.Get(), READ_BUFFER_SIZE, InputStreamOptions_Partial, m_initialReadOp.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = m_initialReadOp->put_Completed(Callback<SocketReadCompletedHandler>(this, &SocketWorker::onReadyRead).Get());
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+
+ HRESULT onReadyRead(IAsyncBufferOperation *asyncInfo, AsyncStatus status)
+ {
+ if (asyncInfo == m_initialReadOp.Get()) {
+ m_initialReadOp.Reset();
+ } else if (asyncInfo == m_readOp.Get()) {
+ m_readOp.Reset();
+ } else {
+ Q_ASSERT(false);
+ }
+
+ // A read in UnconnectedState will close the socket and return -1 and thus tell the caller,
+ // that the connection was closed. The socket cannot be closed here, as the subsequent read
+ // might fail then.
+ if (status == Error || status == Canceled) {
+ emit socketErrorOccured(QBluetoothSocket::NetworkError);
+ return S_OK;
+ }
+
+ ComPtr<IBuffer> buffer;
+ HRESULT hr = asyncInfo->GetResults(&buffer);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to get read results buffer");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+
+ UINT32 bufferLength;
+ hr = buffer->get_Length(&bufferLength);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to get buffer length");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+ // A zero sized buffer length signals, that the remote host closed the connection. The socket
+ // cannot be closed though, as the following read might have socket descriptor -1 and thus and
+ // the closing of the socket won't be communicated to the caller. So only the error is set. The
+ // actual socket close happens inside of read.
+ if (!bufferLength) {
+ emit socketErrorOccured(QBluetoothSocket::NetworkError);
+ return S_OK;
+ }
+
+ ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
+ hr = buffer.As(&byteArrayAccess);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to get cast buffer");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+ byte *data;
+ hr = byteArrayAccess->Buffer(&data);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to access buffer data");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+
+ QByteArray newData(reinterpret_cast<const char*>(data), qint64(bufferLength));
+ QMutexLocker readLocker(&m_mutex);
+ if (m_pendingData.isEmpty())
+ QMetaObject::invokeMethod(this, "notifyAboutNewData", Qt::QueuedConnection);
+ m_pendingData << newData;
+ readLocker.unlock();
+
+ hr = QEventDispatcherWinRT::runOnXamlThread([buffer, this]() {
+ UINT32 readBufferLength;
+ ComPtr<IInputStream> stream;
+ HRESULT hr = m_socket->get_InputStream(&stream);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to obtain input stream");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+
+ // Reuse the stream buffer
+ hr = buffer->get_Capacity(&readBufferLength);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to get buffer capacity");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+ hr = buffer->put_Length(0);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to set buffer length");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+
+ hr = stream->ReadAsync(buffer.Get(), readBufferLength, InputStreamOptions_Partial, &m_readOp);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "onReadyRead(): Could not read into socket stream buffer.");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+ hr = m_readOp->put_Completed(Callback<SocketReadCompletedHandler>(this, &SocketWorker::onReadyRead).Get());
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "onReadyRead(): Failed to set socket read callback.");
+ emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
+ return S_OK;
+ }
+ return S_OK;
+ });
+ Q_ASSERT_SUCCEEDED(hr);
+ return S_OK;
+ }
+
+ void setSocket(ComPtr<IStreamSocket> socket) { m_socket = socket; }
+
+private:
+ ComPtr<IStreamSocket> m_socket;
+ QVector<QByteArray> m_pendingData;
+
+ // Protects pendingData/pendingDatagrams which are accessed from native callbacks
+ QMutex m_mutex;
+
+ ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> m_initialReadOp;
+ ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> m_readOp;
+};
+
+QBluetoothSocketPrivate::QBluetoothSocketPrivate()
+ : socket(-1),
+ socketType(QBluetoothServiceInfo::UnknownProtocol),
+ state(QBluetoothSocket::UnconnectedState),
+ socketError(QBluetoothSocket::NoSocketError),
+ secFlags(QBluetooth::NoSecurity),
+ m_worker(new SocketWorker())
+{
+ connect(m_worker, &SocketWorker::newDataReceived,
+ this, &QBluetoothSocketPrivate::handleNewData, Qt::QueuedConnection);
+ connect(m_worker, &SocketWorker::socketErrorOccured,
+ this, &QBluetoothSocketPrivate::handleError, Qt::QueuedConnection);
+}
+
+QBluetoothSocketPrivate::~QBluetoothSocketPrivate()
+{
+ abort();
+}
+
+bool QBluetoothSocketPrivate::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
+{
+ if (socket != -1) {
+ if (type == socketType)
+ return true;
+ m_socketObject = nullptr;
+ socket = -1;
+ }
+ socketType = type;
+ if (socketType != QBluetoothServiceInfo::RfcommProtocol)
+ return false;
+
+ HRESULT hr;
+ hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocket).Get(), &m_socketObject);
+ if (FAILED(hr) || !m_socketObject) {
+ qErrnoWarning(hr, "ensureNativeSocket: Could not create socket instance");
+ return false;
+ }
+ socket = qintptr(m_socketObject.Get());
+ m_worker->setSocket(m_socketObject);
+
+ return true;
+}
+
+void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
+{
+ Q_Q(QBluetoothSocket);
+ Q_UNUSED(openMode);
+
+ 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("QBluetoothSocketPrivate::connectToService: Could not create hostname.");
+
+ const QString portString = QString::number(port);
+ HStringReference portReference(reinterpret_cast<LPCWSTR>(portString.utf16()));
+
+ hr = m_socketObject->ConnectAsync(remoteHost.Get(), portReference.Get(), &m_connectOp);
+ if (hr == E_ACCESSDENIED) {
+ qErrnoWarning(hr, "QBluetoothSocketPrivate::connectToService: Unable to connect to bluetooth socket."
+ "Please check your manifest capabilities.");
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ return;
+ }
+ Q_ASSERT_SUCCEEDED(hr);
+
+ q->setSocketState(QBluetoothSocket::ConnectingState);
+ q->setOpenMode(openMode);
+ QEventDispatcherWinRT::runOnXamlThread([this]() {
+ HRESULT hr;
+ hr = m_connectOp->put_Completed(Callback<IAsyncActionCompletedHandler>(
+ this, &QBluetoothSocketPrivate::handleConnectOpFinished).Get());
+ RETURN_HR_IF_FAILED("connectToHostByName: Could not register \"connectOp\" callback");
+ return S_OK;
+ });
+}
+
+void QBluetoothSocketPrivate::abort()
+{
+ Q_Q(QBluetoothSocket);
+ if (state == QBluetoothSocket::UnconnectedState)
+ return;
+
+ disconnect(m_worker, &SocketWorker::newDataReceived,
+ this, &QBluetoothSocketPrivate::handleNewData);
+ disconnect(m_worker, &SocketWorker::socketErrorOccured,
+ this, &QBluetoothSocketPrivate::handleError);
+ m_worker->deleteLater();
+
+ if (socket != -1) {
+ m_socketObject = nullptr;
+ socket = -1;
+ }
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+}
+
+QString QBluetoothSocketPrivate::localName() const
+{
+ const QBluetoothAddress address = localAddress();
+ if (address.isNull())
+ return QString();
+
+ QBluetoothLocalDevice device(address);
+ return device.name();
+}
+
+QBluetoothAddress QBluetoothSocketPrivate::localAddress() const
+{
+ HRESULT hr;
+ ComPtr<IStreamSocketInformation> info;
+ hr = m_socketObject->get_Information(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IHostName> localHost;
+ hr = info->get_LocalAddress(&localHost);
+ Q_ASSERT_SUCCEEDED(hr);
+ HString localAddress;
+ hr = localHost->get_CanonicalName(localAddress.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ return QBluetoothAddress(qt_QStringFromHString(localAddress));
+}
+
+quint16 QBluetoothSocketPrivate::localPort() const
+{
+ HRESULT hr;
+ ComPtr<IStreamSocketInformation> info;
+ hr = m_socketObject->get_Information(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ HString localPortString;
+ hr = info->get_LocalPort(localPortString.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ return qt_QStringFromHString(localPortString).toInt();
+}
+
+QString QBluetoothSocketPrivate::peerName() const
+{
+ HRESULT hr;
+ ComPtr<IStreamSocketInformation> info;
+ hr = m_socketObject->get_Information(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IHostName> remoteHost;
+ hr = info->get_RemoteHostName(&remoteHost);
+ Q_ASSERT_SUCCEEDED(hr);
+ HString remoteHostName;
+ hr = remoteHost->get_DisplayName(remoteHostName.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ return qt_QStringFromHString(remoteHostName);
+}
+
+QBluetoothAddress QBluetoothSocketPrivate::peerAddress() const
+{
+ HRESULT hr;
+ ComPtr<IStreamSocketInformation> info;
+ hr = m_socketObject->get_Information(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IHostName> remoteHost;
+ hr = info->get_RemoteAddress(&remoteHost);
+ Q_ASSERT_SUCCEEDED(hr);
+ HString remoteAddress;
+ hr = remoteHost->get_CanonicalName(remoteAddress.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ return QBluetoothAddress(qt_QStringFromHString(remoteAddress));
+}
+
+quint16 QBluetoothSocketPrivate::peerPort() const
+{
+ HRESULT hr;
+ ComPtr<IStreamSocketInformation> info;
+ hr = m_socketObject->get_Information(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ HString remotePortString;
+ hr = info->get_LocalPort(remotePortString.GetAddressOf());
+ Q_ASSERT_SUCCEEDED(hr);
+ return qt_QStringFromHString(remotePortString).toInt();
+}
+
+qint64 QBluetoothSocketPrivate::writeData(const char *data, qint64 maxSize)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (state != QBluetoothSocket::ConnectedState) {
+ errorString = QBluetoothSocket::tr("Cannot write while not connected");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return -1;
+ }
+
+ ComPtr<IOutputStream> stream;
+ HRESULT hr;
+ hr = m_socketObject->get_OutputStream(&stream);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ qint64 bytesWritten = writeIOStream(stream, data, maxSize);
+ if (bytesWritten < 0) {
+ qCWarning(QT_BT_WINRT) << "Socket::writeData: " << state;
+ errorString = QBluetoothSocket::tr("Cannot read while not connected");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ }
+
+ emit q->bytesWritten(bytesWritten);
+ return bytesWritten;
+}
+
+qint64 QBluetoothSocketPrivate::readData(char *data, qint64 maxSize)
+{
+ Q_Q(QBluetoothSocket);
+
+ if (state != QBluetoothSocket::ConnectedState) {
+ errorString = QBluetoothSocket::tr("Cannot read while not connected");
+ q->setSocketError(QBluetoothSocket::OperationError);
+ return -1;
+ }
+
+ if (!buffer.isEmpty())
+ return buffer.read(data, maxSize);
+
+ return 0;
+}
+
+void QBluetoothSocketPrivate::close()
+{
+ abort();
+}
+
+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;
+}
+
+qint64 QBluetoothSocketPrivate::bytesAvailable() const
+{
+ return buffer.size();
+}
+
+void QBluetoothSocketPrivate::handleNewData(const QVector<QByteArray> &data)
+{
+ // Defer putting the data into the list until the next event loop iteration
+ // (where the readyRead signal is emitted as well)
+ QMetaObject::invokeMethod(this, "putIntoPendingData", Qt::QueuedConnection,
+ Q_ARG(QVector<QByteArray>, data));
+}
+
+void QBluetoothSocketPrivate::handleError(QBluetoothSocket::SocketError error)
+{
+ Q_Q(QBluetoothSocket);
+ switch (error) {
+ case QBluetoothSocket::NetworkError:
+ errorString = QBluetoothSocket::tr("Network error");
+ break;
+ default:
+ errorString = QBluetoothSocket::tr("Unknown socket error");
+ }
+
+ q->setSocketError(error);
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+}
+
+void QBluetoothSocketPrivate::addToPendingData(const QVector<QByteArray> &data)
+{
+ Q_Q(QBluetoothSocket);
+ QMutexLocker locker(&m_readMutex);
+ m_pendingData.append(data);
+ for (const QByteArray &newData : data) {
+ char *writePointer = buffer.reserve(newData.length());
+ memcpy(writePointer, newData.data(), newData.length());
+ }
+ locker.unlock();
+ emit q->readyRead();
+}
+
+HRESULT QBluetoothSocketPrivate::handleConnectOpFinished(ABI::Windows::Foundation::IAsyncAction *action, ABI::Windows::Foundation::AsyncStatus status)
+{
+ Q_Q(QBluetoothSocket);
+ if (status != Completed || !m_connectOp) { // Protect against a late callback
+ errorString = QBluetoothSocket::tr("Unknown socket error");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ return S_OK;
+ }
+
+ HRESULT hr = action->GetResults();
+ switch (hr) {
+ case 0x8007274c: // A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
+ errorString = QBluetoothSocket::tr("Connection timed out");
+ q->setSocketError(QBluetoothSocket::NetworkError);
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ return S_OK;
+ case 0x80072751: // A socket operation was attempted to an unreachable host.
+ errorString = QBluetoothSocket::tr("Host not reachable");
+ q->setSocketError(QBluetoothSocket::HostNotFoundError);
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ return S_OK;
+ case 0x8007274d: // No connection could be made because the target machine actively refused it.
+ errorString = QBluetoothSocket::tr("Host refused connection");
+ q->setSocketError(QBluetoothSocket::HostNotFoundError);
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ return S_OK;
+ default:
+ if (FAILED(hr)) {
+ errorString = QBluetoothSocket::tr("Unknown socket error");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ return S_OK;
+ }
+ }
+
+ // The callback might be triggered several times if we do not cancel/reset it here
+ if (m_connectOp) {
+ ComPtr<IAsyncInfo> info;
+ hr = m_connectOp.As(&info);
+ Q_ASSERT_SUCCEEDED(hr);
+ if (info) {
+ hr = info->Cancel();
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = info->Close();
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+ hr = m_connectOp.Reset();
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+
+ q->setSocketState(QBluetoothSocket::ConnectedState);
+ m_worker->startReading();
+ emit q->connected();
+
+ return S_OK;
+}
+
+QT_END_NAMESPACE
+
+#include "qbluetoothsocket_winrt.moc"