blob: 48b147579fbb9bbbcceebf7b4254607e910f1030 [file] [log] [blame]
/****************************************************************************
**
** 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_winrt_p.h"
#ifdef CLASSIC_APP_BUILD
#define Q_OS_WINRT
#endif
#include <qfunctions_winrt.h>
#include <private/qeventdispatcher_winrt_p.h>
#include <QtBluetooth/QBluetoothLocalDevice>
#include <QtBluetooth/qbluetoothdeviceinfo.h>
#include <QtBluetooth/qbluetoothserviceinfo.h>
#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);
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> buffer;
if (len > UINT32_MAX) {
qCWarning(QT_BT_WINRT) << "writeIOStream can only write up to" << UINT32_MAX << "bytes.";
len = UINT32_MAX;
}
quint32 ulen = static_cast<quint32>(len);
HRESULT hr = g->bufferFactory->Create(ulen, &buffer);
Q_ASSERT_SUCCEEDED(hr);
hr = buffer->put_Length(ulen);
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, ulen);
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()
{
}
void close()
{
m_shuttingDown = true;
if (Q_UNLIKELY(m_initialReadOp)) {
onReadyRead(m_initialReadOp.Get(), Canceled);
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);
}
m_initialReadOp.Reset();
}
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 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()
{
HRESULT hr;
hr = QEventDispatcherWinRT::runOnXamlThread([this]()
{
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);
return S_OK;
});
Q_ASSERT_SUCCEEDED(hr);
}
HRESULT onReadyRead(IAsyncBufferOperation *asyncInfo, AsyncStatus status)
{
if (m_shuttingDown)
return S_OK;
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::RemoteHostClosedError);
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), int(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;
bool m_shuttingDown = false;
// Protects pendingData/pendingDatagrams which are accessed from native callbacks
QMutex m_mutex;
ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> m_initialReadOp;
ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> m_readOp;
};
QBluetoothSocketPrivateWinRT::QBluetoothSocketPrivateWinRT()
: m_worker(new SocketWorker())
{
secFlags = QBluetooth::NoSecurity;
connect(m_worker, &SocketWorker::newDataReceived,
this, &QBluetoothSocketPrivateWinRT::handleNewData, Qt::QueuedConnection);
connect(m_worker, &SocketWorker::socketErrorOccured,
this, &QBluetoothSocketPrivateWinRT::handleError, Qt::QueuedConnection);
}
QBluetoothSocketPrivateWinRT::~QBluetoothSocketPrivateWinRT()
{
abort();
}
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::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::UnconnectedState);
return;
}
Q_ASSERT_SUCCEEDED(hr);
q->setSocketState(QBluetoothSocket::ConnectingState);
requestedOpenMode = openMode;
hr = QEventDispatcherWinRT::runOnXamlThread([this]() {
HRESULT hr;
hr = m_connectOp->put_Completed(Callback<IAsyncActionCompletedHandler>(
this, &QBluetoothSocketPrivateWinRT::handleConnectOpFinished).Get());
RETURN_HR_IF_FAILED("connectToHostByName: Could not register \"connectOp\" callback");
return S_OK;
});
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<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::UnconnectedState
&& q->state() != QBluetoothSocket::ServiceLookupState) {
qCWarning(QT_BT_WINRT) << "QBluetoothSocket::connectToService called on busy socket";
errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
q->setSocketError(QBluetoothSocket::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::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::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::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::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::SerialPort)) {
qCWarning(QT_BT_WINRT) << "No port, no PSM, and no UUID provided. Unable to connect";
return;
}
qCDebug(QT_BT_WINRT) << "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::UnconnectedState) {
qCWarning(QT_BT_WINRT) << "QBluetoothSocketPrivateWinRT::connectToService called on busy socket";
errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
q->setSocketError(QBluetoothSocket::OperationError);
return;
}
if (q->socketType() != QBluetoothServiceInfo::RfcommProtocol) {
errorString = QBluetoothSocket::tr("Socket type not supported");
q->setSocketError(QBluetoothSocket::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::UnconnectedState) {
qCWarning(QT_BT_WINRT) << "QBluetoothSocketPrivateWinRT::connectToService called on busy socket";
errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
q->setSocketError(QBluetoothSocket::OperationError);
return;
}
if (q->socketType() != QBluetoothServiceInfo::RfcommProtocol) {
errorString = QBluetoothSocket::tr("Socket type not supported");
q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
return;
}
connectToServiceHelper(address, port, openMode);
}
void QBluetoothSocketPrivateWinRT::abort()
{
Q_Q(QBluetoothSocket);
if (state == QBluetoothSocket::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::ConnectedState;
q->setSocketState(QBluetoothSocket::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();
}
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);
HString localAddress;
hr = localHost->get_CanonicalName(localAddress.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
return QBluetoothAddress(qt_QStringFromHString(localAddress));
}
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_WINRT) << "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);
HString remoteHostName;
hr = remoteHost->get_DisplayName(remoteHostName.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
return qt_QStringFromHString(remoteHostName);
}
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);
HString remoteAddress;
hr = remoteHost->get_CanonicalName(remoteAddress.GetAddressOf());
Q_ASSERT_SUCCEEDED(hr);
return QBluetoothAddress(qt_QStringFromHString(remoteAddress));
}
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_LocalPort(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_WINRT) << "Unexpected remote port";
return 0;
}
return quint16(port);
}
qint64 QBluetoothSocketPrivateWinRT::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 QBluetoothSocketPrivateWinRT::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()) {
if (maxSize > INT_MAX)
maxSize = INT_MAX;
return buffer.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_WINRT) << "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::ConnectedState)
m_worker->startReading();
q->setOpenMode(openMode);
return true;
}
qint64 QBluetoothSocketPrivateWinRT::bytesAvailable() const
{
return buffer.size();
}
qint64 QBluetoothSocketPrivateWinRT::bytesToWrite() const
{
return 0; // nothing because always unbuffered
}
bool QBluetoothSocketPrivateWinRT::canReadLine() const
{
return buffer.canReadLine();
}
void QBluetoothSocketPrivateWinRT::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, "addToPendingData", Qt::QueuedConnection,
Q_ARG(QVector<QByteArray>, data));
}
void QBluetoothSocketPrivateWinRT::handleError(QBluetoothSocket::SocketError error)
{
Q_Q(QBluetoothSocket);
switch (error) {
case QBluetoothSocket::NetworkError:
errorString = QBluetoothSocket::tr("Network error");
break;
case QBluetoothSocket::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::ConnectedState;
q->setSocketState(QBluetoothSocket::UnconnectedState);
if (wasConnected) {
q->setOpenMode(QIODevice::NotOpen);
emit q->readChannelFinished();
}
}
void QBluetoothSocketPrivateWinRT::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(), size_t(newData.length()));
}
locker.unlock();
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::UnknownSocketError);
q->setSocketState(QBluetoothSocket::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::NetworkError);
q->setSocketState(QBluetoothSocket::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::HostNotFoundError);
q->setSocketState(QBluetoothSocket::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::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);
}
m_connectOp.Reset();
}
q->setOpenMode(requestedOpenMode);
q->setSocketState(QBluetoothSocket::ConnectedState);
m_worker->startReading();
return S_OK;
}
QT_END_NAMESPACE
#include "qbluetoothsocket_winrt.moc"