Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qbluetoothsocket_winrt.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6
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>
13
14#include <robuffer.h>
15#include <windows.devices.bluetooth.h>
16#include <windows.networking.sockets.h>
17#include <windows.storage.streams.h>
18#include <wrl.h>
19
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;
26using namespace ABI::Windows::Networking;
27using namespace ABI::Windows::Networking::Sockets;
28using namespace ABI::Windows::Storage::Streams;
29
32
34
36
38{
40 {
41 HRESULT hr;
42 hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
43 &bufferFactory);
44 Q_ASSERT_SUCCEEDED(hr);
45 }
46
48};
49Q_GLOBAL_STATIC(SocketGlobal, g)
50
51#define READ_BUFFER_SIZE 65536
52
53static inline QString qt_QStringFromHString(const HString &string)
54{
55 UINT32 length;
56 PCWSTR rawString = string.GetRawBuffer(&length);
57 if (length > INT_MAX)
58 length = INT_MAX;
59 return QString::fromWCharArray(rawString, int(length));
60}
61
62static qint64 writeIOStream(ComPtr<IOutputStream> stream, const char *data, qint64 len)
63{
64 ComPtr<IBuffer> tempBuffer;
65 if (len > UINT32_MAX) {
66 qCWarning(QT_BT_WINDOWS) << "writeIOStream can only write up to" << UINT32_MAX << "bytes.";
67 len = UINT32_MAX;
68 }
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);
77 byte *bytes;
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);
84 UINT32 bytesWritten;
85 hr = QWinRTFunctions::await(op, &bytesWritten);
86 RETURN_IF_FAILED("Failed to write to stream", return -1);
87 return bytesWritten;
88}
89
90class SocketWorker : public QObject
91{
93public:
95 {
96 }
97
99 {
100 }
101 void close()
102 {
103 m_shuttingDown = true;
104 if (m_readOp) {
109 if (info) {
110 hr = info->Cancel();
112 hr = info->Close();
114 }
115 m_readOp.Reset();
116 }
117 }
118
119signals:
121 void socketErrorOccured(QBluetoothSocket::SocketError error);
122
123public slots:
130
131public:
152
154 {
155 if (m_shuttingDown)
156 return S_OK;
157
158 if (asyncInfo == m_readOp.Get())
159 m_readOp.Reset();
160 else
161 Q_ASSERT(false);
162
163 // A read in UnconnectedState will close the socket and return -1 and thus tell the caller,
164 // that the connection was closed. The socket cannot be closed here, as the subsequent read
165 // might fail then.
166 if (status == Error || status == Canceled) {
168 return S_OK;
169 }
170
173 if (FAILED(hr)) {
174 qErrnoWarning(hr, "Failed to get read results buffer");
176 return S_OK;
177 }
178
181 if (FAILED(hr)) {
182 qErrnoWarning(hr, "Failed to get buffer length");
184 return S_OK;
185 }
186 // A zero sized buffer length signals, that the remote host closed the connection. The socket
187 // cannot be closed though, as the following read might have socket descriptor -1 and thus and
188 // the closing of the socket won't be communicated to the caller. So only the error is set. The
189 // actual socket close happens inside of read.
190 if (!bufferLength) {
192 return S_OK;
193 }
194
197 if (FAILED(hr)) {
198 qErrnoWarning(hr, "Failed to get cast buffer");
200 return S_OK;
201 }
202 byte *data;
204 if (FAILED(hr)) {
205 qErrnoWarning(hr, "Failed to access buffer data");
207 return S_OK;
208 }
209
210 QByteArray newData(reinterpret_cast<const char*>(data), int(bufferLength));
212 QMetaObject::invokeMethod(this, "notifyAboutNewData", Qt::QueuedConnection);
214
218 if (FAILED(hr)) {
219 qErrnoWarning(hr, "Failed to obtain input stream");
221 return S_OK;
222 }
223
224 // Reuse the stream buffer
226 if (FAILED(hr)) {
227 qErrnoWarning(hr, "Failed to get buffer capacity");
229 return S_OK;
230 }
232 if (FAILED(hr)) {
233 qErrnoWarning(hr, "Failed to set buffer length");
235 return S_OK;
236 }
237
239 if (FAILED(hr)) {
240 qErrnoWarning(hr, "onReadyRead(): Could not read into socket stream buffer.");
242 return S_OK;
243 }
248 if (thisPtr)
250 return S_OK;
251 }).Get());
252 if (FAILED(hr)) {
253 qErrnoWarning(hr, "onReadyRead(): Failed to set socket read callback.");
255 return S_OK;
256 }
257 return S_OK;
258 }
259
261
262private:
264 QList<QByteArray> m_pendingData;
265 bool m_shuttingDown = false;
266
267 ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> m_readOp;
268};
269
270QBluetoothSocketPrivateWinRT::QBluetoothSocketPrivateWinRT(QBluetoothSocket *qq)
271 : QBluetoothSocketBasePrivate{qq},
272 m_worker(new SocketWorker())
273{
274 threadCoInit(this);
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);
280}
281
282QBluetoothSocketPrivateWinRT::~QBluetoothSocketPrivateWinRT()
283{
284 abort();
286}
287
288bool QBluetoothSocketPrivateWinRT::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
289{
290 if (socket != -1) {
291 if (type == socketType)
292 return true;
293 m_socketObject = nullptr;
294 socket = -1;
295 }
296 socketType = type;
297 if (socketType != QBluetoothServiceInfo::RfcommProtocol)
298 return false;
299
300 HRESULT hr;
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");
304 return false;
305 }
306 socket = qintptr(m_socketObject.Get());
307 m_worker->setSocket(m_socketObject);
308
309 return true;
310}
311
312void QBluetoothSocketPrivateWinRT::connectToService(Microsoft::WRL::ComPtr<IHostName> hostName,
313 const QString &serviceName,
314 QIODevice::OpenMode openMode)
315{
316 Q_Q(QBluetoothSocket);
317
318 if (socket == -1 && !ensureNativeSocket(socketType)) {
319 errorString = QBluetoothSocket::tr("Unknown socket error");
320 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
321 return;
322 }
323
324 HStringReference serviceNameReference(reinterpret_cast<LPCWSTR>(serviceName.utf16()));
325
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);
331 return;
332 }
333 Q_ASSERT_SUCCEEDED(hr);
334
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");
340 return;
341}
342
343void QBluetoothSocketPrivateWinRT::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
344{
345 Q_Q(QBluetoothSocket);
346
347 if (socket == -1 && !ensureNativeSocket(socketType)) {
348 errorString = QBluetoothSocket::tr("Unknown socket error");
349 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
350 return;
351 }
352
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(),
357 &hostNameFactory);
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);
364}
365
366void QBluetoothSocketPrivateWinRT::connectToService(
367 const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
368{
369 Q_Q(QBluetoothSocket);
370
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);
376 return;
377 }
378
379 // we are checking the service protocol and not socketType()
380 // socketType will change in ensureNativeSocket()
381 if (service.socketProtocol() != QBluetoothServiceInfo::RfcommProtocol) {
382 errorString = QBluetoothSocket::tr("Socket type not supported");
383 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
384 return;
385 }
386
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);
391
392 if (!ensureNativeSocket(QBluetoothServiceInfo::L2capProtocol)) {
393 errorString = QBluetoothSocket::tr("Unknown socket error");
394 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
395 return;
396 }
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);
404 return;
405 }
406 HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(connectionHostName.utf16()));
407 ComPtr<IHostNameFactory> hostNameFactory;
408 HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
409 &hostNameFactory);
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);
416
417 if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) {
418 errorString = QBluetoothSocket::tr("Unknown socket error");
419 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
420 return;
421 }
422 connectToServiceHelper(service.device().address(), quint16(service.serverChannel()),
423 openMode);
424 } else {
425 // try doing service discovery to see if we can find the socket
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";
429 return;
430 }
431 qCDebug(QT_BT_WINDOWS) << "Need a port/psm, doing discovery";
432 q->doDeviceDiscovery(service, openMode);
433 }
434}
435
436void QBluetoothSocketPrivateWinRT::connectToService(
437 const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode)
438{
439 Q_Q(QBluetoothSocket);
440
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);
445 return;
446 }
447
448 if (q->socketType() != QBluetoothServiceInfo::RfcommProtocol) {
449 errorString = QBluetoothSocket::tr("Socket type not supported");
450 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
451 return;
452 }
453
454 QBluetoothServiceInfo service;
455 QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::MiscellaneousDevice);
456 service.setDevice(device);
457 service.setServiceUuid(uuid);
458 q->doDeviceDiscovery(service, openMode);
459}
460
461void QBluetoothSocketPrivateWinRT::connectToService(
462 const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
463{
464 Q_Q(QBluetoothSocket);
465
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);
470 return;
471 }
472
473 if (q->socketType() != QBluetoothServiceInfo::RfcommProtocol) {
474 errorString = QBluetoothSocket::tr("Socket type not supported");
475 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
476 return;
477 }
478
479 connectToServiceHelper(address, port, openMode);
480}
481
482void QBluetoothSocketPrivateWinRT::abort()
483{
484 Q_Q(QBluetoothSocket);
485 if (state == QBluetoothSocket::SocketState::UnconnectedState)
486 return;
487
488 disconnect(m_worker, &SocketWorker::newDataReceived,
489 this, &QBluetoothSocketPrivateWinRT::handleNewData);
490 disconnect(m_worker, &SocketWorker::socketErrorOccured,
491 this, &QBluetoothSocketPrivateWinRT::handleError);
492 m_worker->close();
493 m_worker->deleteLater();
494
495 if (socket != -1) {
496 m_socketObject = nullptr;
497 socket = -1;
498 }
499
500 const bool wasConnected = q->state() == QBluetoothSocket::SocketState::ConnectedState;
501 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
502 if (wasConnected) {
503 q->setOpenMode(QIODevice::NotOpen);
504 emit q->readChannelFinished();
505 }
506}
507
508QString QBluetoothSocketPrivateWinRT::localName() const
509{
510 const QBluetoothAddress address = localAddress();
511 if (address.isNull())
512 return QString();
513
514 QBluetoothLocalDevice device(address);
515 return device.name();
516}
517
518static QString fromWinApiAddress(HString address)
519{
520 // WinAPI returns address with parentheses around it. We need to remove
521 // them to convert to QBluetoothAddress.
522 QString addressStr(qt_QStringFromHString(address));
523 if (addressStr.startsWith(QLatin1Char('(')) && addressStr.endsWith(QLatin1Char(')'))) {
524 addressStr = addressStr.sliced(1, addressStr.size() - 2);
525 }
526 return addressStr;
527}
528
529QBluetoothAddress QBluetoothSocketPrivateWinRT::localAddress() const
530{
531 if (!m_socketObject)
532 return QBluetoothAddress();
533
534 HRESULT hr;
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);
541 if (localHost) {
542 HString localAddress;
543 hr = localHost->get_CanonicalName(localAddress.GetAddressOf());
544 Q_ASSERT_SUCCEEDED(hr);
545 return QBluetoothAddress(fromWinApiAddress(std::move(localAddress)));
546 }
547 return QBluetoothAddress();
548}
549
550quint16 QBluetoothSocketPrivateWinRT::localPort() const
551{
552 if (!m_socketObject)
553 return 0;
554
555 HRESULT hr;
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);
562 bool ok = true;
563 const uint port = qt_QStringFromHString(localPortString).toUInt(&ok);
564 if (!ok || port > UINT16_MAX) {
565 qCWarning(QT_BT_WINDOWS) << "Unexpected local port";
566 return 0;
567 }
568 return quint16(port);
569}
570
571QString QBluetoothSocketPrivateWinRT::peerName() const
572{
573 if (!m_socketObject)
574 return QString();
575
576 HRESULT hr;
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);
583 if (remoteHost) {
584 HString remoteHostName;
585 hr = remoteHost->get_DisplayName(remoteHostName.GetAddressOf());
586 Q_ASSERT_SUCCEEDED(hr);
587 return qt_QStringFromHString(remoteHostName);
588 }
589 return {};
590}
591
592QBluetoothAddress QBluetoothSocketPrivateWinRT::peerAddress() const
593{
594 if (!m_socketObject)
595 return QBluetoothAddress();
596
597 HRESULT hr;
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);
604 if (remoteHost) {
605 HString remoteAddress;
606 hr = remoteHost->get_CanonicalName(remoteAddress.GetAddressOf());
607 Q_ASSERT_SUCCEEDED(hr);
608 return QBluetoothAddress(fromWinApiAddress(std::move(remoteAddress)));
609 }
610 return QBluetoothAddress();
611}
612
613quint16 QBluetoothSocketPrivateWinRT::peerPort() const
614{
615 if (!m_socketObject)
616 return 0;
617
618 HRESULT hr;
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);
625 bool ok = true;
626 const uint port = qt_QStringFromHString(remotePortString).toUInt(&ok);
627 if (!ok || port > UINT16_MAX) {
628 qCWarning(QT_BT_WINDOWS) << "Unexpected remote port";
629 return 0;
630 }
631 return quint16(port);
632}
633
634qint64 QBluetoothSocketPrivateWinRT::writeData(const char *data, qint64 maxSize)
635{
636 Q_Q(QBluetoothSocket);
637
638 if (state != QBluetoothSocket::SocketState::ConnectedState) {
639 errorString = QBluetoothSocket::tr("Cannot write while not connected");
640 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
641 return -1;
642 }
643
644 ComPtr<IOutputStream> stream;
645 HRESULT hr;
646 hr = m_socketObject->get_OutputStream(&stream);
647 Q_ASSERT_SUCCEEDED(hr);
648
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);
654 }
655
656 emit q->bytesWritten(bytesWritten);
657 return bytesWritten;
658}
659
660qint64 QBluetoothSocketPrivateWinRT::readData(char *data, qint64 maxSize)
661{
662 Q_Q(QBluetoothSocket);
663
664 if (state != QBluetoothSocket::SocketState::ConnectedState) {
665 errorString = QBluetoothSocket::tr("Cannot read while not connected");
666 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
667 return -1;
668 }
669
670 if (!rxBuffer.isEmpty()) {
671 if (maxSize > INT_MAX)
672 maxSize = INT_MAX;
673 return rxBuffer.read(data, int(maxSize));
674 }
675
676 return 0;
677}
678
679void QBluetoothSocketPrivateWinRT::close()
680{
681 abort();
682}
683
684bool QBluetoothSocketPrivateWinRT::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
685 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
686{
687 Q_UNUSED(socketDescriptor);
688 Q_UNUSED(socketType);
689 Q_UNUSED(socketState);
690 Q_UNUSED(openMode);
691 qCWarning(QT_BT_WINDOWS) << "No socket descriptor support on WinRT.";
692 return false;
693}
694
695bool QBluetoothSocketPrivateWinRT::setSocketDescriptor(ComPtr<IStreamSocket> socketPtr, QBluetoothServiceInfo::Protocol socketType,
696 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
697{
698 Q_Q(QBluetoothSocket);
699 if (socketType != QBluetoothServiceInfo::RfcommProtocol || !socketPtr)
700 return false;
701
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();
708 // QBluetoothSockets are unbuffered on Windows
709 q->setOpenMode(openMode | QIODevice::Unbuffered);
710 return true;
711}
712
713qint64 QBluetoothSocketPrivateWinRT::bytesAvailable() const
714{
715 return rxBuffer.size();
716}
717
718qint64 QBluetoothSocketPrivateWinRT::bytesToWrite() const
719{
720 return 0; // nothing because always unbuffered
721}
722
723bool QBluetoothSocketPrivateWinRT::canReadLine() const
724{
725 return rxBuffer.canReadLine();
726}
727
728void QBluetoothSocketPrivateWinRT::handleNewData(const QList<QByteArray> &data)
729{
730 // Defer putting the data into the list until the next event loop iteration
731 // (where the readyRead signal is emitted as well)
732 QMetaObject::invokeMethod(this, "addToPendingData", Qt::QueuedConnection,
733 Q_ARG(QList<QByteArray>, data));
734}
735
736void QBluetoothSocketPrivateWinRT::handleError(QBluetoothSocket::SocketError error)
737{
738 Q_Q(QBluetoothSocket);
739 switch (error) {
740 case QBluetoothSocket::SocketError::NetworkError:
741 errorString = QBluetoothSocket::tr("Network error");
742 break;
743 case QBluetoothSocket::SocketError::RemoteHostClosedError:
744 errorString = QBluetoothSocket::tr("Remote host closed connection");
745 break;
746 default:
747 errorString = QBluetoothSocket::tr("Unknown socket error");
748 }
749
750 q->setSocketError(error);
751 const bool wasConnected = q->state() == QBluetoothSocket::SocketState::ConnectedState;
752 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
753 if (wasConnected) {
754 q->setOpenMode(QIODevice::NotOpen);
755 emit q->readChannelFinished();
756 }
757}
758
759void QBluetoothSocketPrivateWinRT::addToPendingData(const QList<QByteArray> &data)
760{
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()));
765 }
766 emit q->readyRead();
767}
768
769HRESULT QBluetoothSocketPrivateWinRT::handleConnectOpFinished(ABI::Windows::Foundation::IAsyncAction *action, ABI::Windows::Foundation::AsyncStatus status)
770{
771 Q_Q(QBluetoothSocket);
772 if (status != Completed || !m_connectOp) { // Protect against a late callback
773 errorString = QBluetoothSocket::tr("Unknown socket error");
774 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
775 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
776 return S_OK;
777 }
778
779 HRESULT hr = action->GetResults();
780 switch (hr) {
781
782 // A connection attempt failed because the connected party did not properly respond after a
783 // period of time, or established connection failed because connected host has failed to respond.
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);
788 return S_OK;
789 // A socket operation was attempted to an unreachable host.
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);
794 return S_OK;
795 // No connection could be made because the target machine actively refused it.
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);
800 return S_OK;
801 default:
802 if (FAILED(hr)) {
803 errorString = QBluetoothSocket::tr("Unknown socket error");
804 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
805 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
806 return S_OK;
807 }
808 }
809
810 // The callback might be triggered several times if we do not cancel/reset it here
811 if (m_connectOp) {
812 ComPtr<IAsyncInfo> info;
813 hr = m_connectOp.As(&info);
814 Q_ASSERT_SUCCEEDED(hr);
815 if (info) {
816 hr = info->Cancel();
817 Q_ASSERT_SUCCEEDED(hr);
818 hr = info->Close();
819 Q_ASSERT_SUCCEEDED(hr);
820 }
821 m_connectOp.Reset();
822 }
823
824 // QBluetoothSockets are unbuffered on Windows
825 q->setOpenMode(requestedOpenMode | QIODevice::Unbuffered);
826 q->setSocketState(QBluetoothSocket::SocketState::ConnectedState);
827 m_worker->startReading();
828
829 return S_OK;
830}
831
832QT_END_NAMESPACE
833
834#include "qbluetoothsocket_winrt.moc"
void socketErrorOccured(QBluetoothSocket::SocketError error)
static qint64 writeIOStream(ComPtr< IOutputStream > stream, const char *data, qint64 len)
#define READ_BUFFER_SIZE
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