Revision control

Copy as Markdown

// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qbluetoothsocket_winrt_p.h"
#include "qbluetoothutils_winrt_p.h"
#include <QtBluetooth/QBluetoothLocalDevice>
#include <QtBluetooth/qbluetoothdeviceinfo.h>
#include <QtBluetooth/qbluetoothserviceinfo.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/QPointer>
#include <QtCore/private/qfunctions_winrt_p.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_WINDOWS)
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);
if (length > INT_MAX)
length = INT_MAX;
return QString::fromWCharArray(rawString, int(length));
}
static qint64 writeIOStream(ComPtr<IOutputStream> stream, const char *data, qint64 len)
{
ComPtr<IBuffer> tempBuffer;
if (len > UINT32_MAX) {
qCWarning(QT_BT_WINDOWS) << "writeIOStream can only write up to" << UINT32_MAX << "bytes.";
len = UINT32_MAX;
}
quint32 ulen = static_cast<quint32>(len);
HRESULT hr = g->bufferFactory->Create(ulen, &tempBuffer);
Q_ASSERT_SUCCEEDED(hr);
hr = tempBuffer->put_Length(ulen);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
hr = tempBuffer.As(&byteArrayAccess);
Q_ASSERT_SUCCEEDED(hr);
byte *bytes;
hr = byteArrayAccess->Buffer(&bytes);
Q_ASSERT_SUCCEEDED(hr);
memcpy(bytes, data, ulen);
ComPtr<IAsyncOperationWithProgress<UINT32, UINT32>> op;
hr = stream->WriteAsync(tempBuffer.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()
{
}
void close()
{
m_shuttingDown = true;
if (m_readOp) {
onReadyRead(m_readOp.Get(), Canceled);
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);
}
m_readOp.Reset();
}
}
signals:
void newDataReceived(const QList<QByteArray> &data);
void socketErrorOccured(QBluetoothSocket::SocketError error);
public slots:
Q_INVOKABLE void notifyAboutNewData()
{
const QList<QByteArray> newData = std::move(m_pendingData);
m_pendingData.clear();
emit newDataReceived(newData);
}
public:
void startReading()
{
ComPtr<IBuffer> tempBuffer;
HRESULT hr = g->bufferFactory->Create(READ_BUFFER_SIZE, &tempBuffer);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IInputStream> stream;
hr = m_socket->get_InputStream(&stream);
Q_ASSERT_SUCCEEDED(hr);
hr = stream->ReadAsync(tempBuffer.Get(), READ_BUFFER_SIZE, InputStreamOptions_Partial, m_readOp.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
QPointer<SocketWorker> thisPtr(this);
hr = m_readOp->put_Completed(
Callback<SocketReadCompletedHandler>([thisPtr](IAsyncBufferOperation *asyncInfo,
AsyncStatus status) {
if (thisPtr)
return thisPtr->onReadyRead(asyncInfo, status);
return S_OK;
}).Get());
Q_ASSERT_SUCCEEDED(hr);
}
HRESULT onReadyRead(IAsyncBufferOperation *asyncInfo, AsyncStatus status)
{
if (m_shuttingDown)
return S_OK;
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::SocketError::NetworkError);
return S_OK;
}
ComPtr<IBuffer> tempBuffer;
HRESULT hr = asyncInfo->GetResults(&tempBuffer);
if (FAILED(hr)) {
qErrnoWarning(hr, "Failed to get read results buffer");
emit socketErrorOccured(QBluetoothSocket::SocketError::UnknownSocketError);
return S_OK;
}
UINT32 bufferLength;
hr = tempBuffer->get_Length(&bufferLength);
if (FAILED(hr)) {
qErrnoWarning(hr, "Failed to get buffer length");
emit socketErrorOccured(QBluetoothSocket::SocketError::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::SocketError::RemoteHostClosedError);
return S_OK;
}
ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
hr = tempBuffer.As(&byteArrayAccess);
if (FAILED(hr)) {
qErrnoWarning(hr, "Failed to get cast buffer");
emit socketErrorOccured(QBluetoothSocket::SocketError::UnknownSocketError);
return S_OK;
}
byte *data;
hr = byteArrayAccess->Buffer(&data);
if (FAILED(hr)) {
qErrnoWarning(hr, "Failed to access buffer data");
emit socketErrorOccured(QBluetoothSocket::SocketError::UnknownSocketError);
return S_OK;
}
QByteArray newData(reinterpret_cast<const char*>(data), int(bufferLength));
if (m_pendingData.isEmpty())
QMetaObject::invokeMethod(this, "notifyAboutNewData", Qt::QueuedConnection);
m_pendingData << newData;
UINT32 readBufferLength;
ComPtr<IInputStream> stream;
hr = m_socket->get_InputStream(&stream);
if (FAILED(hr)) {
qErrnoWarning(hr, "Failed to obtain input stream");
emit socketErrorOccured(QBluetoothSocket::SocketError::UnknownSocketError);
return S_OK;
}
// Reuse the stream buffer
hr = tempBuffer->get_Capacity(&readBufferLength);
if (FAILED(hr)) {
qErrnoWarning(hr, "Failed to get buffer capacity");
emit socketErrorOccured(QBluetoothSocket::SocketError::UnknownSocketError);
return S_OK;
}
hr = tempBuffer->put_Length(0);
if (FAILED(hr)) {
qErrnoWarning(hr, "Failed to set buffer length");
emit socketErrorOccured(QBluetoothSocket::SocketError::UnknownSocketError);
return S_OK;
}
hr = stream->ReadAsync(tempBuffer.Get(), readBufferLength, InputStreamOptions_Partial, &m_readOp);
if (FAILED(hr)) {
qErrnoWarning(hr, "onReadyRead(): Could not read into socket stream buffer.");
emit socketErrorOccured(QBluetoothSocket::SocketError::UnknownSocketError);
return S_OK;
}
QPointer<SocketWorker> thisPtr(this);
hr = m_readOp->put_Completed(
Callback<SocketReadCompletedHandler>([thisPtr](IAsyncBufferOperation *asyncInfo,
AsyncStatus status) {
if (thisPtr)
return thisPtr->onReadyRead(asyncInfo, status);
return S_OK;
}).Get());
if (FAILED(hr)) {
qErrnoWarning(hr, "onReadyRead(): Failed to set socket read callback.");
emit socketErrorOccured(QBluetoothSocket::SocketError::UnknownSocketError);
return S_OK;
}
return S_OK;
}
void setSocket(ComPtr<IStreamSocket> socket) { m_socket = socket; }
private:
ComPtr<IStreamSocket> m_socket;
QList<QByteArray> m_pendingData;
bool m_shuttingDown = false;
ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> m_readOp;
};
QBluetoothSocketPrivateWinRT::QBluetoothSocketPrivateWinRT(QBluetoothSocket *qq)
: QBluetoothSocketBasePrivate{qq},
m_worker(new SocketWorker())
{
threadCoInit(this);
secFlags = QBluetooth::Security::NoSecurity;
connect(m_worker, &SocketWorker::newDataReceived,
this, &QBluetoothSocketPrivateWinRT::handleNewData, Qt::QueuedConnection);
connect(m_worker, &SocketWorker::socketErrorOccured,
this, &QBluetoothSocketPrivateWinRT::handleError, Qt::QueuedConnection);
}
QBluetoothSocketPrivateWinRT::~QBluetoothSocketPrivateWinRT()
{
abort();
threadCoUninit(this);
}
bool QBluetoothSocketPrivateWinRT::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 QBluetoothSocketPrivateWinRT::connectToService(Microsoft::WRL::ComPtr<IHostName> hostName,
const QString &serviceName,
QIODevice::OpenMode openMode)
{
Q_Q(QBluetoothSocket);
if (socket == -1 && !ensureNativeSocket(socketType)) {
errorString = QBluetoothSocket::tr("Unknown socket error");
q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
return;
}
HStringReference serviceNameReference(reinterpret_cast<LPCWSTR>(serviceName.utf16()));
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.");
q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
return;
}
Q_ASSERT_SUCCEEDED(hr);
q->setSocketState(QBluetoothSocket::SocketState::ConnectingState);
requestedOpenMode = openMode;
hr = m_connectOp->put_Completed(Callback<IAsyncActionCompletedHandler>(
this, &QBluetoothSocketPrivateWinRT::handleConnectOpFinished).Get());
RETURN_VOID_IF_FAILED("connectToHostByName: Could not register \"connectOp\" callback");
return;
}
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::SocketError::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)
{
Q_Q(QBluetoothSocket);
if (q->state() != QBluetoothSocket::SocketState::UnconnectedState
&& q->state() != QBluetoothSocket::SocketState::ServiceLookupState) {
qCWarning(QT_BT_WINDOWS) << "QBluetoothSocket::connectToService called on busy socket";
errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
q->setSocketError(QBluetoothSocket::SocketError::OperationError);
return;
}
// we are checking the service protocol and not socketType()
// socketType will change in ensureNativeSocket()
if (service.socketProtocol() != QBluetoothServiceInfo::RfcommProtocol) {
errorString = QBluetoothSocket::tr("Socket type not supported");
q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
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);
if (!ensureNativeSocket(QBluetoothServiceInfo::L2capProtocol)) {
errorString = QBluetoothSocket::tr("Unknown socket error");
q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
return;
}
connectToServiceHelper(service.device().address(),
quint16(service.protocolServiceMultiplexer()), openMode);
} 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::SocketError::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)) {
errorString = QBluetoothSocket::tr("Unknown socket error");
q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
return;
}
connectToServiceHelper(service.device().address(), quint16(service.serverChannel()),
openMode);
} else {
// try doing service discovery to see if we can find the socket
if (service.serviceUuid().isNull()
&& !service.serviceClassUuids().contains(QBluetoothUuid::ServiceClassUuid::SerialPort)) {
qCWarning(QT_BT_WINDOWS) << "No port, no PSM, and no UUID provided. Unable to connect";
return;
}
qCDebug(QT_BT_WINDOWS) << "Need a port/psm, doing discovery";
q->doDeviceDiscovery(service, openMode);
}
}
void QBluetoothSocketPrivateWinRT::connectToService(
const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode)
{
Q_Q(QBluetoothSocket);
if (q->state() != QBluetoothSocket::SocketState::UnconnectedState) {
qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWinRT::connectToService called on busy socket";
errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
q->setSocketError(QBluetoothSocket::SocketError::OperationError);
return;
}
if (q->socketType() != QBluetoothServiceInfo::RfcommProtocol) {
errorString = QBluetoothSocket::tr("Socket type not supported");
q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
return;
}
QBluetoothServiceInfo service;
QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::MiscellaneousDevice);
service.setDevice(device);
service.setServiceUuid(uuid);
q->doDeviceDiscovery(service, openMode);
}
void QBluetoothSocketPrivateWinRT::connectToService(
const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
{
Q_Q(QBluetoothSocket);
if (q->state() != QBluetoothSocket::SocketState::UnconnectedState) {
qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWinRT::connectToService called on busy socket";
errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
q->setSocketError(QBluetoothSocket::SocketError::OperationError);
return;
}
if (q->socketType() != QBluetoothServiceInfo::RfcommProtocol) {
errorString = QBluetoothSocket::tr("Socket type not supported");
q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
return;
}
connectToServiceHelper(address, port, openMode);
}
void QBluetoothSocketPrivateWinRT::abort()
{
Q_Q(QBluetoothSocket);
if (state == QBluetoothSocket::SocketState::UnconnectedState)
return;
disconnect(m_worker, &SocketWorker::newDataReceived,
this, &QBluetoothSocketPrivateWinRT::handleNewData);
disconnect(m_worker, &SocketWorker::socketErrorOccured,
this, &QBluetoothSocketPrivateWinRT::handleError);
m_worker->close();
m_worker->deleteLater();
if (socket != -1) {
m_socketObject = nullptr;
socket = -1;
}
const bool wasConnected = q->state() == QBluetoothSocket::SocketState::ConnectedState;
q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
if (wasConnected) {
q->setOpenMode(QIODevice::NotOpen);
emit q->readChannelFinished();
}
}
QString QBluetoothSocketPrivateWinRT::localName() const
{
const QBluetoothAddress address = localAddress();
if (address.isNull())
return QString();
QBluetoothLocalDevice device(address);
return device.name();
}
static QString fromWinApiAddress(HString address)
{
// WinAPI returns address with parentheses around it. We need to remove
// them to convert to QBluetoothAddress.
QString addressStr(qt_QStringFromHString(address));
if (addressStr.startsWith(QLatin1Char('(')) && addressStr.endsWith(QLatin1Char(')'))) {
addressStr = addressStr.sliced(1, addressStr.size() - 2);
}
return addressStr;
}
QBluetoothAddress QBluetoothSocketPrivateWinRT::localAddress() const
{
if (!m_socketObject)
return QBluetoothAddress();
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);
if (localHost) {
HString localAddress;
hr = localHost->get_CanonicalName(localAddress.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
return QBluetoothAddress(fromWinApiAddress(std::move(localAddress)));
}
return QBluetoothAddress();
}
quint16 QBluetoothSocketPrivateWinRT::localPort() const
{
if (!m_socketObject)
return 0;
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);
bool ok = true;
const uint port = qt_QStringFromHString(localPortString).toUInt(&ok);
if (!ok || port > UINT16_MAX) {
qCWarning(QT_BT_WINDOWS) << "Unexpected local port";
return 0;
}
return quint16(port);
}
QString QBluetoothSocketPrivateWinRT::peerName() const
{
if (!m_socketObject)
return QString();
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);
if (remoteHost) {
HString remoteHostName;
hr = remoteHost->get_DisplayName(remoteHostName.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
return qt_QStringFromHString(remoteHostName);
}
return {};
}
QBluetoothAddress QBluetoothSocketPrivateWinRT::peerAddress() const
{
if (!m_socketObject)
return QBluetoothAddress();
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);
if (remoteHost) {
HString remoteAddress;
hr = remoteHost->get_CanonicalName(remoteAddress.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
return QBluetoothAddress(fromWinApiAddress(std::move(remoteAddress)));
}
return QBluetoothAddress();
}
quint16 QBluetoothSocketPrivateWinRT::peerPort() const
{
if (!m_socketObject)
return 0;
HRESULT hr;
ComPtr<IStreamSocketInformation> info;
hr = m_socketObject->get_Information(&info);
Q_ASSERT_SUCCEEDED(hr);
HString remotePortString;
hr = info->get_RemotePort(remotePortString.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
bool ok = true;
const uint port = qt_QStringFromHString(remotePortString).toUInt(&ok);
if (!ok || port > UINT16_MAX) {
qCWarning(QT_BT_WINDOWS) << "Unexpected remote port";
return 0;
}
return quint16(port);
}
qint64 QBluetoothSocketPrivateWinRT::writeData(const char *data, qint64 maxSize)
{
Q_Q(QBluetoothSocket);
if (state != QBluetoothSocket::SocketState::ConnectedState) {
errorString = QBluetoothSocket::tr("Cannot write while not connected");
q->setSocketError(QBluetoothSocket::SocketError::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_WINDOWS) << "Socket::writeData: " << state;
errorString = QBluetoothSocket::tr("Cannot read while not connected");
q->setSocketError(QBluetoothSocket::SocketError::OperationError);
}
emit q->bytesWritten(bytesWritten);
return bytesWritten;
}
qint64 QBluetoothSocketPrivateWinRT::readData(char *data, qint64 maxSize)
{
Q_Q(QBluetoothSocket);
if (state != QBluetoothSocket::SocketState::ConnectedState) {
errorString = QBluetoothSocket::tr("Cannot read while not connected");
q->setSocketError(QBluetoothSocket::SocketError::OperationError);
return -1;
}
if (!rxBuffer.isEmpty()) {
if (maxSize > INT_MAX)
maxSize = INT_MAX;
return rxBuffer.read(data, int(maxSize));
}
return 0;
}
void QBluetoothSocketPrivateWinRT::close()
{
abort();
}
bool QBluetoothSocketPrivateWinRT::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
{
Q_UNUSED(socketDescriptor);
Q_UNUSED(socketType);
Q_UNUSED(socketState);
Q_UNUSED(openMode);
qCWarning(QT_BT_WINDOWS) << "No socket descriptor support on WinRT.";
return false;
}
bool QBluetoothSocketPrivateWinRT::setSocketDescriptor(ComPtr<IStreamSocket> socketPtr, QBluetoothServiceInfo::Protocol socketType,
QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
{
Q_Q(QBluetoothSocket);
if (socketType != QBluetoothServiceInfo::RfcommProtocol || !socketPtr)
return false;
m_socketObject = socketPtr;
socket = qintptr(m_socketObject.Get());
m_worker->setSocket(m_socketObject);
q->setSocketState(socketState);
if (socketState == QBluetoothSocket::SocketState::ConnectedState)
m_worker->startReading();
// QBluetoothSockets are unbuffered on Windows
q->setOpenMode(openMode | QIODevice::Unbuffered);
return true;
}
qint64 QBluetoothSocketPrivateWinRT::bytesAvailable() const
{
return rxBuffer.size();
}
qint64 QBluetoothSocketPrivateWinRT::bytesToWrite() const
{
return 0; // nothing because always unbuffered
}
bool QBluetoothSocketPrivateWinRT::canReadLine() const
{
return rxBuffer.canReadLine();
}
void QBluetoothSocketPrivateWinRT::handleNewData(const QList<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, "addToPendingData", Qt::QueuedConnection,
Q_ARG(QList<QByteArray>, data));
}
void QBluetoothSocketPrivateWinRT::handleError(QBluetoothSocket::SocketError error)
{
Q_Q(QBluetoothSocket);
switch (error) {
case QBluetoothSocket::SocketError::NetworkError:
errorString = QBluetoothSocket::tr("Network error");
break;
case QBluetoothSocket::SocketError::RemoteHostClosedError:
errorString = QBluetoothSocket::tr("Remote host closed connection");
break;
default:
errorString = QBluetoothSocket::tr("Unknown socket error");
}
q->setSocketError(error);
const bool wasConnected = q->state() == QBluetoothSocket::SocketState::ConnectedState;
q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
if (wasConnected) {
q->setOpenMode(QIODevice::NotOpen);
emit q->readChannelFinished();
}
}
void QBluetoothSocketPrivateWinRT::addToPendingData(const QList<QByteArray> &data)
{
Q_Q(QBluetoothSocket);
for (const QByteArray &newData : data) {
char *writePointer = rxBuffer.reserve(newData.length());
memcpy(writePointer, newData.data(), size_t(newData.length()));
}
emit q->readyRead();
}
HRESULT QBluetoothSocketPrivateWinRT::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::SocketError::UnknownSocketError);
q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
return S_OK;
}
HRESULT hr = action->GetResults();
switch (hr) {
// 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.
case HRESULT_FROM_WIN32(WSAETIMEDOUT):
errorString = QBluetoothSocket::tr("Connection timed out");
q->setSocketError(QBluetoothSocket::SocketError::NetworkError);
q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
return S_OK;
// A socket operation was attempted to an unreachable host.
case HRESULT_FROM_WIN32(WSAEHOSTUNREACH):
errorString = QBluetoothSocket::tr("Host not reachable");
q->setSocketError(QBluetoothSocket::SocketError::HostNotFoundError);
q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
return S_OK;
// No connection could be made because the target machine actively refused it.
case HRESULT_FROM_WIN32(WSAECONNREFUSED):
errorString = QBluetoothSocket::tr("Host refused connection");
q->setSocketError(QBluetoothSocket::SocketError::HostNotFoundError);
q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
return S_OK;
default:
if (FAILED(hr)) {
errorString = QBluetoothSocket::tr("Unknown socket error");
q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
q->setSocketState(QBluetoothSocket::SocketState::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);
}
m_connectOp.Reset();
}
// QBluetoothSockets are unbuffered on Windows
q->setOpenMode(requestedOpenMode | QIODevice::Unbuffered);
q->setSocketState(QBluetoothSocket::SocketState::ConnectedState);
m_worker->startReading();
return S_OK;
}
QT_END_NAMESPACE
#include "qbluetoothsocket_winrt.moc"