7#include <QtBluetooth/QBluetoothLocalDevice>
8#include <QtBluetooth/qbluetoothdeviceinfo.h>
9#include <QtBluetooth/qbluetoothserviceinfo.h>
10#include <QtCore/qloggingcategory.h>
11#include <QtCore/QPointer>
12#include <QtCore/private/qfunctions_winrt_p.h>
15#include <windows.devices.bluetooth.h>
16#include <windows.networking.sockets.h>
17#include <windows.storage.streams.h>
20using namespace Microsoft::WRL;
21using namespace Microsoft::WRL::Wrappers;
22using namespace ABI::Windows::Devices::Bluetooth;
23using namespace ABI::Windows::Devices::Bluetooth::Rfcomm;
24using namespace ABI::Windows::Foundation;
25using namespace ABI::Windows::Foundation::Collections;
27using namespace ABI::Windows::Networking::Sockets;
42 hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
44 Q_ASSERT_SUCCEEDED(hr);
51#define READ_BUFFER_SIZE 65536
56 PCWSTR rawString = string.GetRawBuffer(&length);
59 return QString::fromWCharArray(rawString,
int(length));
64 ComPtr<IBuffer> tempBuffer;
65 if (len > UINT32_MAX) {
66 qCWarning(QT_BT_WINDOWS) <<
"writeIOStream can only write up to" << UINT32_MAX <<
"bytes.";
69 quint32 ulen =
static_cast<quint32>(len);
70 HRESULT hr = g->bufferFactory->Create(ulen, &tempBuffer);
71 Q_ASSERT_SUCCEEDED(hr);
72 hr = tempBuffer->put_Length(ulen);
73 Q_ASSERT_SUCCEEDED(hr);
74 ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
75 hr = tempBuffer.As(&byteArrayAccess);
76 Q_ASSERT_SUCCEEDED(hr);
78 hr = byteArrayAccess->Buffer(&bytes);
79 Q_ASSERT_SUCCEEDED(hr);
80 memcpy(bytes, data, ulen);
81 ComPtr<IAsyncOperationWithProgress<UINT32, UINT32>> op;
82 hr = stream->WriteAsync(tempBuffer.Get(), &op);
83 RETURN_IF_FAILED(
"Failed to write to stream",
return -1);
85 hr = QWinRTFunctions::await(op, &bytesWritten);
86 RETURN_IF_FAILED(
"Failed to write to stream",
return -1);
240 qErrnoWarning(
hr,
"onReadyRead(): Could not read into socket stream buffer.");
253 qErrnoWarning(
hr,
"onReadyRead(): Failed to set socket read callback.");
264 QList<QByteArray> m_pendingData;
265 bool m_shuttingDown =
false;
267 ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> m_readOp;
270QBluetoothSocketPrivateWinRT::QBluetoothSocketPrivateWinRT(QBluetoothSocket *qq)
271 : QBluetoothSocketBasePrivate{qq},
272 m_worker(
new SocketWorker())
275 secFlags = QBluetooth::Security::NoSecurity;
276 connect(m_worker, &SocketWorker::newDataReceived,
277 this, &QBluetoothSocketPrivateWinRT::handleNewData, Qt::QueuedConnection);
278 connect(m_worker, &SocketWorker::socketErrorOccured,
279 this, &QBluetoothSocketPrivateWinRT::handleError, Qt::QueuedConnection);
282QBluetoothSocketPrivateWinRT::~QBluetoothSocketPrivateWinRT()
288bool QBluetoothSocketPrivateWinRT::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
291 if (type == socketType)
293 m_socketObject =
nullptr;
297 if (socketType != QBluetoothServiceInfo::RfcommProtocol)
301 hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocket).Get(), &m_socketObject);
302 if (FAILED(hr) || !m_socketObject) {
303 qErrnoWarning(hr,
"ensureNativeSocket: Could not create socket instance");
306 socket = qintptr(m_socketObject.Get());
307 m_worker->setSocket(m_socketObject);
312void QBluetoothSocketPrivateWinRT::connectToService(
Microsoft::
WRL::
ComPtr<IHostName> hostName,
313 const QString &serviceName,
314 QIODevice::OpenMode openMode)
316 Q_Q(QBluetoothSocket);
318 if (socket == -1 && !ensureNativeSocket(socketType)) {
319 errorString = QBluetoothSocket::tr(
"Unknown socket error");
320 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
324 HStringReference serviceNameReference(
reinterpret_cast<LPCWSTR>(serviceName.utf16()));
326 HRESULT hr = m_socketObject->ConnectAsync(hostName.Get(), serviceNameReference.Get(), &m_connectOp);
327 if (hr == E_ACCESSDENIED) {
328 qErrnoWarning(hr,
"QBluetoothSocketPrivateWinRT::connectToService: Unable to connect to bluetooth socket."
329 "Please check your manifest capabilities.");
330 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
333 Q_ASSERT_SUCCEEDED(hr);
335 q->setSocketState(QBluetoothSocket::SocketState::ConnectingState);
336 requestedOpenMode = openMode;
337 hr = m_connectOp->put_Completed(Callback<IAsyncActionCompletedHandler>(
338 this, &QBluetoothSocketPrivateWinRT::handleConnectOpFinished).Get());
339 RETURN_VOID_IF_FAILED(
"connectToHostByName: Could not register \"connectOp\" callback");
343void QBluetoothSocketPrivateWinRT::connectToServiceHelper(
const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
345 Q_Q(QBluetoothSocket);
347 if (socket == -1 && !ensureNativeSocket(socketType)) {
348 errorString = QBluetoothSocket::tr(
"Unknown socket error");
349 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
353 const QString addressString = address.toString();
354 HStringReference hostNameRef(
reinterpret_cast<LPCWSTR>(addressString.utf16()));
355 ComPtr<IHostNameFactory> hostNameFactory;
356 HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
358 Q_ASSERT_SUCCEEDED(hr);
359 ComPtr<IHostName> remoteHost;
360 hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
361 RETURN_VOID_IF_FAILED(
"QBluetoothSocketPrivateWinRT::connectToService: Could not create hostname.");
362 const QString portString = QString::number(port);
363 connectToService(remoteHost, portString, openMode);
366void QBluetoothSocketPrivateWinRT::connectToService(
367 const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
369 Q_Q(QBluetoothSocket);
371 if (q->state() != QBluetoothSocket::SocketState::UnconnectedState
372 && q->state() != QBluetoothSocket::SocketState::ServiceLookupState) {
373 qCWarning(QT_BT_WINDOWS) <<
"QBluetoothSocket::connectToService called on busy socket";
374 errorString = QBluetoothSocket::tr(
"Trying to connect while connection is in progress");
375 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
381 if (service.socketProtocol() != QBluetoothServiceInfo::RfcommProtocol) {
382 errorString = QBluetoothSocket::tr(
"Socket type not supported");
383 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
387 const QString connectionHostName = service.attribute(0xBEEF).toString();
388 const QString connectionServiceName = service.attribute(0xBEF0).toString();
389 if (service.protocolServiceMultiplexer() > 0) {
390 Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::L2capProtocol);
392 if (!ensureNativeSocket(QBluetoothServiceInfo::L2capProtocol)) {
393 errorString = QBluetoothSocket::tr(
"Unknown socket error");
394 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
397 connectToServiceHelper(service.device().address(),
398 quint16(service.protocolServiceMultiplexer()), openMode);
399 }
else if (!connectionHostName.isEmpty() && !connectionServiceName.isEmpty()) {
400 Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::RfcommProtocol);
401 if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) {
402 errorString = QBluetoothSocket::tr(
"Unknown socket error");
403 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
406 HStringReference hostNameRef(
reinterpret_cast<LPCWSTR>(connectionHostName.utf16()));
407 ComPtr<IHostNameFactory> hostNameFactory;
408 HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
410 Q_ASSERT_SUCCEEDED(hr);
411 ComPtr<IHostName> remoteHost;
412 hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
413 connectToService(remoteHost, connectionServiceName, openMode);
414 }
else if (service.serverChannel() > 0) {
415 Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::RfcommProtocol);
417 if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) {
418 errorString = QBluetoothSocket::tr(
"Unknown socket error");
419 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
422 connectToServiceHelper(service.device().address(), quint16(service.serverChannel()),
426 if (service.serviceUuid().isNull()
427 && !service.serviceClassUuids().contains(QBluetoothUuid::ServiceClassUuid::SerialPort)) {
428 qCWarning(QT_BT_WINDOWS) <<
"No port, no PSM, and no UUID provided. Unable to connect";
431 qCDebug(QT_BT_WINDOWS) <<
"Need a port/psm, doing discovery";
432 q->doDeviceDiscovery(service, openMode);
436void QBluetoothSocketPrivateWinRT::connectToService(
437 const QBluetoothAddress &address,
const QBluetoothUuid &uuid, QIODevice::OpenMode openMode)
439 Q_Q(QBluetoothSocket);
441 if (q->state() != QBluetoothSocket::SocketState::UnconnectedState) {
442 qCWarning(QT_BT_WINDOWS) <<
"QBluetoothSocketPrivateWinRT::connectToService called on busy socket";
443 errorString = QBluetoothSocket::tr(
"Trying to connect while connection is in progress");
444 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
448 if (q->socketType() != QBluetoothServiceInfo::RfcommProtocol) {
449 errorString = QBluetoothSocket::tr(
"Socket type not supported");
450 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
454 QBluetoothServiceInfo service;
455 QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::MiscellaneousDevice);
456 service.setDevice(device);
457 service.setServiceUuid(uuid);
458 q->doDeviceDiscovery(service, openMode);
461void QBluetoothSocketPrivateWinRT::connectToService(
462 const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
464 Q_Q(QBluetoothSocket);
466 if (q->state() != QBluetoothSocket::SocketState::UnconnectedState) {
467 qCWarning(QT_BT_WINDOWS) <<
"QBluetoothSocketPrivateWinRT::connectToService called on busy socket";
468 errorString = QBluetoothSocket::tr(
"Trying to connect while connection is in progress");
469 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
473 if (q->socketType() != QBluetoothServiceInfo::RfcommProtocol) {
474 errorString = QBluetoothSocket::tr(
"Socket type not supported");
475 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
479 connectToServiceHelper(address, port, openMode);
482void QBluetoothSocketPrivateWinRT::abort()
484 Q_Q(QBluetoothSocket);
485 if (state == QBluetoothSocket::SocketState::UnconnectedState)
488 disconnect(m_worker, &SocketWorker::newDataReceived,
489 this, &QBluetoothSocketPrivateWinRT::handleNewData);
490 disconnect(m_worker, &SocketWorker::socketErrorOccured,
491 this, &QBluetoothSocketPrivateWinRT::handleError);
493 m_worker->deleteLater();
496 m_socketObject =
nullptr;
500 const bool wasConnected = q->state() == QBluetoothSocket::SocketState::ConnectedState;
501 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
503 q->setOpenMode(QIODevice::NotOpen);
504 emit q->readChannelFinished();
508QString QBluetoothSocketPrivateWinRT::localName()
const
510 const QBluetoothAddress address = localAddress();
511 if (address.isNull())
514 QBluetoothLocalDevice device(address);
515 return device.name();
522 QString addressStr(qt_QStringFromHString(address));
523 if (addressStr.startsWith(QLatin1Char(
'(')) && addressStr.endsWith(QLatin1Char(
')'))) {
524 addressStr = addressStr.sliced(1, addressStr.size() - 2);
529QBluetoothAddress QBluetoothSocketPrivateWinRT::localAddress()
const
532 return QBluetoothAddress();
535 ComPtr<IStreamSocketInformation> info;
536 hr = m_socketObject->get_Information(&info);
537 Q_ASSERT_SUCCEEDED(hr);
538 ComPtr<IHostName> localHost;
539 hr = info->get_LocalAddress(&localHost);
540 Q_ASSERT_SUCCEEDED(hr);
542 HString localAddress;
543 hr = localHost->get_CanonicalName(localAddress.GetAddressOf());
544 Q_ASSERT_SUCCEEDED(hr);
545 return QBluetoothAddress(fromWinApiAddress(std::move(localAddress)));
547 return QBluetoothAddress();
550quint16 QBluetoothSocketPrivateWinRT::localPort()
const
556 ComPtr<IStreamSocketInformation> info;
557 hr = m_socketObject->get_Information(&info);
558 Q_ASSERT_SUCCEEDED(hr);
559 HString localPortString;
560 hr = info->get_LocalPort(localPortString.GetAddressOf());
561 Q_ASSERT_SUCCEEDED(hr);
563 const uint port = qt_QStringFromHString(localPortString).toUInt(&ok);
564 if (!ok || port > UINT16_MAX) {
565 qCWarning(QT_BT_WINDOWS) <<
"Unexpected local port";
568 return quint16(port);
571QString QBluetoothSocketPrivateWinRT::peerName()
const
577 ComPtr<IStreamSocketInformation> info;
578 hr = m_socketObject->get_Information(&info);
579 Q_ASSERT_SUCCEEDED(hr);
580 ComPtr<IHostName> remoteHost;
581 hr = info->get_RemoteHostName(&remoteHost);
582 Q_ASSERT_SUCCEEDED(hr);
584 HString remoteHostName;
585 hr = remoteHost->get_DisplayName(remoteHostName.GetAddressOf());
586 Q_ASSERT_SUCCEEDED(hr);
587 return qt_QStringFromHString(remoteHostName);
592QBluetoothAddress QBluetoothSocketPrivateWinRT::peerAddress()
const
595 return QBluetoothAddress();
598 ComPtr<IStreamSocketInformation> info;
599 hr = m_socketObject->get_Information(&info);
600 Q_ASSERT_SUCCEEDED(hr);
601 ComPtr<IHostName> remoteHost;
602 hr = info->get_RemoteAddress(&remoteHost);
603 Q_ASSERT_SUCCEEDED(hr);
605 HString remoteAddress;
606 hr = remoteHost->get_CanonicalName(remoteAddress.GetAddressOf());
607 Q_ASSERT_SUCCEEDED(hr);
608 return QBluetoothAddress(fromWinApiAddress(std::move(remoteAddress)));
610 return QBluetoothAddress();
613quint16 QBluetoothSocketPrivateWinRT::peerPort()
const
619 ComPtr<IStreamSocketInformation> info;
620 hr = m_socketObject->get_Information(&info);
621 Q_ASSERT_SUCCEEDED(hr);
622 HString remotePortString;
623 hr = info->get_RemotePort(remotePortString.GetAddressOf());
624 Q_ASSERT_SUCCEEDED(hr);
626 const uint port = qt_QStringFromHString(remotePortString).toUInt(&ok);
627 if (!ok || port > UINT16_MAX) {
628 qCWarning(QT_BT_WINDOWS) <<
"Unexpected remote port";
631 return quint16(port);
634qint64 QBluetoothSocketPrivateWinRT::writeData(
const char *data, qint64 maxSize)
636 Q_Q(QBluetoothSocket);
638 if (state != QBluetoothSocket::SocketState::ConnectedState) {
639 errorString = QBluetoothSocket::tr(
"Cannot write while not connected");
640 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
644 ComPtr<IOutputStream> stream;
646 hr = m_socketObject->get_OutputStream(&stream);
647 Q_ASSERT_SUCCEEDED(hr);
649 qint64 bytesWritten = writeIOStream(stream, data, maxSize);
650 if (bytesWritten < 0) {
651 qCWarning(QT_BT_WINDOWS) <<
"Socket::writeData: " << state;
652 errorString = QBluetoothSocket::tr(
"Cannot read while not connected");
653 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
656 emit q->bytesWritten(bytesWritten);
660qint64 QBluetoothSocketPrivateWinRT::readData(
char *data, qint64 maxSize)
662 Q_Q(QBluetoothSocket);
664 if (state != QBluetoothSocket::SocketState::ConnectedState) {
665 errorString = QBluetoothSocket::tr(
"Cannot read while not connected");
666 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
670 if (!rxBuffer.isEmpty()) {
671 if (maxSize > INT_MAX)
673 return rxBuffer.read(data,
int(maxSize));
679void QBluetoothSocketPrivateWinRT::close()
684bool QBluetoothSocketPrivateWinRT::setSocketDescriptor(
int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
685 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
687 Q_UNUSED(socketDescriptor);
688 Q_UNUSED(socketType);
689 Q_UNUSED(socketState);
691 qCWarning(QT_BT_WINDOWS) <<
"No socket descriptor support on WinRT.";
695bool QBluetoothSocketPrivateWinRT::setSocketDescriptor(ComPtr<IStreamSocket> socketPtr, QBluetoothServiceInfo::Protocol socketType,
696 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
698 Q_Q(QBluetoothSocket);
699 if (socketType != QBluetoothServiceInfo::RfcommProtocol || !socketPtr)
702 m_socketObject = socketPtr;
703 socket = qintptr(m_socketObject.Get());
704 m_worker->setSocket(m_socketObject);
705 q->setSocketState(socketState);
706 if (socketState == QBluetoothSocket::SocketState::ConnectedState)
707 m_worker->startReading();
709 q->setOpenMode(openMode | QIODevice::Unbuffered);
713qint64 QBluetoothSocketPrivateWinRT::bytesAvailable()
const
715 return rxBuffer.size();
718qint64 QBluetoothSocketPrivateWinRT::bytesToWrite()
const
723bool QBluetoothSocketPrivateWinRT::canReadLine()
const
725 return rxBuffer.canReadLine();
728void QBluetoothSocketPrivateWinRT::handleNewData(
const QList<QByteArray> &data)
732 QMetaObject::invokeMethod(
this,
"addToPendingData", Qt::QueuedConnection,
733 Q_ARG(QList<QByteArray>, data));
736void QBluetoothSocketPrivateWinRT::handleError(QBluetoothSocket::SocketError error)
738 Q_Q(QBluetoothSocket);
740 case QBluetoothSocket::SocketError::NetworkError:
741 errorString = QBluetoothSocket::tr(
"Network error");
743 case QBluetoothSocket::SocketError::RemoteHostClosedError:
744 errorString = QBluetoothSocket::tr(
"Remote host closed connection");
747 errorString = QBluetoothSocket::tr(
"Unknown socket error");
750 q->setSocketError(error);
751 const bool wasConnected = q->state() == QBluetoothSocket::SocketState::ConnectedState;
752 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
754 q->setOpenMode(QIODevice::NotOpen);
755 emit q->readChannelFinished();
759void QBluetoothSocketPrivateWinRT::addToPendingData(
const QList<QByteArray> &data)
761 Q_Q(QBluetoothSocket);
762 for (
const QByteArray &newData : data) {
763 char *writePointer = rxBuffer.reserve(newData.length());
764 memcpy(writePointer, newData.data(), size_t(newData.length()));
769HRESULT QBluetoothSocketPrivateWinRT::handleConnectOpFinished(ABI::Windows::Foundation::IAsyncAction *action, ABI::Windows::Foundation::AsyncStatus status)
771 Q_Q(QBluetoothSocket);
772 if (status != Completed || !m_connectOp) {
773 errorString = QBluetoothSocket::tr(
"Unknown socket error");
774 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
775 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
779 HRESULT hr = action->GetResults();
784 case HRESULT_FROM_WIN32(WSAETIMEDOUT):
785 errorString = QBluetoothSocket::tr(
"Connection timed out");
786 q->setSocketError(QBluetoothSocket::SocketError::NetworkError);
787 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
790 case HRESULT_FROM_WIN32(WSAEHOSTUNREACH):
791 errorString = QBluetoothSocket::tr(
"Host not reachable");
792 q->setSocketError(QBluetoothSocket::SocketError::HostNotFoundError);
793 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
796 case HRESULT_FROM_WIN32(WSAECONNREFUSED):
797 errorString = QBluetoothSocket::tr(
"Host refused connection");
798 q->setSocketError(QBluetoothSocket::SocketError::HostNotFoundError);
799 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
803 errorString = QBluetoothSocket::tr(
"Unknown socket error");
804 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
805 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
812 ComPtr<IAsyncInfo> info;
813 hr = m_connectOp.As(&info);
814 Q_ASSERT_SUCCEEDED(hr);
817 Q_ASSERT_SUCCEEDED(hr);
819 Q_ASSERT_SUCCEEDED(hr);
825 q->setOpenMode(requestedOpenMode | QIODevice::Unbuffered);
826 q->setSocketState(QBluetoothSocket::SocketState::ConnectedState);
827 m_worker->startReading();
834#include "qbluetoothsocket_winrt.moc"
void socketErrorOccured(QBluetoothSocket::SocketError error)
static qint64 writeIOStream(ComPtr< IOutputStream > stream, const char *data, qint64 len)
IAsyncOperationWithProgress< IBuffer *, UINT32 > IAsyncBufferOperation
static QString fromWinApiAddress(HString address)
static QString qt_QStringFromHString(const HString &string)
IAsyncOperationWithProgressCompletedHandler< IBuffer *, UINT32 > SocketReadCompletedHandler
void threadCoUninit(void *caller)
void threadCoInit(void *caller)
QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcQIORing)
ComPtr< IBufferFactory > bufferFactory