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()
271 : m_worker(new SocketWorker())
272{
274 secFlags = QBluetooth::Security::NoSecurity;
275 connect(m_worker, &SocketWorker::newDataReceived,
276 this, &QBluetoothSocketPrivateWinRT::handleNewData, Qt::QueuedConnection);
277 connect(m_worker, &SocketWorker::socketErrorOccured,
278 this, &QBluetoothSocketPrivateWinRT::handleError, Qt::QueuedConnection);
279}
280
281QBluetoothSocketPrivateWinRT::~QBluetoothSocketPrivateWinRT()
282{
283 abort();
285}
286
287bool QBluetoothSocketPrivateWinRT::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
288{
289 if (socket != -1) {
290 if (type == socketType)
291 return true;
292 m_socketObject = nullptr;
293 socket = -1;
294 }
295 socketType = type;
296 if (socketType != QBluetoothServiceInfo::RfcommProtocol)
297 return false;
298
299 HRESULT hr;
300 hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocket).Get(), &m_socketObject);
301 if (FAILED(hr) || !m_socketObject) {
302 qErrnoWarning(hr, "ensureNativeSocket: Could not create socket instance");
303 return false;
304 }
305 socket = qintptr(m_socketObject.Get());
306 m_worker->setSocket(m_socketObject);
307
308 return true;
309}
310
311void QBluetoothSocketPrivateWinRT::connectToService(Microsoft::WRL::ComPtr<IHostName> hostName,
312 const QString &serviceName,
313 QIODevice::OpenMode openMode)
314{
315 Q_Q(QBluetoothSocket);
316
317 if (socket == -1 && !ensureNativeSocket(socketType)) {
318 errorString = QBluetoothSocket::tr("Unknown socket error");
319 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
320 return;
321 }
322
323 HStringReference serviceNameReference(reinterpret_cast<LPCWSTR>(serviceName.utf16()));
324
325 HRESULT hr = m_socketObject->ConnectAsync(hostName.Get(), serviceNameReference.Get(), &m_connectOp);
326 if (hr == E_ACCESSDENIED) {
327 qErrnoWarning(hr, "QBluetoothSocketPrivateWinRT::connectToService: Unable to connect to bluetooth socket."
328 "Please check your manifest capabilities.");
329 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
330 return;
331 }
332 Q_ASSERT_SUCCEEDED(hr);
333
334 q->setSocketState(QBluetoothSocket::SocketState::ConnectingState);
335 requestedOpenMode = openMode;
336 hr = m_connectOp->put_Completed(Callback<IAsyncActionCompletedHandler>(
337 this, &QBluetoothSocketPrivateWinRT::handleConnectOpFinished).Get());
338 RETURN_VOID_IF_FAILED("connectToHostByName: Could not register \"connectOp\" callback");
339 return;
340}
341
342void QBluetoothSocketPrivateWinRT::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
343{
344 Q_Q(QBluetoothSocket);
345
346 if (socket == -1 && !ensureNativeSocket(socketType)) {
347 errorString = QBluetoothSocket::tr("Unknown socket error");
348 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
349 return;
350 }
351
352 const QString addressString = address.toString();
353 HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(addressString.utf16()));
354 ComPtr<IHostNameFactory> hostNameFactory;
355 HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
356 &hostNameFactory);
357 Q_ASSERT_SUCCEEDED(hr);
358 ComPtr<IHostName> remoteHost;
359 hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
360 RETURN_VOID_IF_FAILED("QBluetoothSocketPrivateWinRT::connectToService: Could not create hostname.");
361 const QString portString = QString::number(port);
362 connectToService(remoteHost, portString, openMode);
363}
364
365void QBluetoothSocketPrivateWinRT::connectToService(
366 const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
367{
368 Q_Q(QBluetoothSocket);
369
370 if (q->state() != QBluetoothSocket::SocketState::UnconnectedState
371 && q->state() != QBluetoothSocket::SocketState::ServiceLookupState) {
372 qCWarning(QT_BT_WINDOWS) << "QBluetoothSocket::connectToService called on busy socket";
373 errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
374 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
375 return;
376 }
377
378 // we are checking the service protocol and not socketType()
379 // socketType will change in ensureNativeSocket()
380 if (service.socketProtocol() != QBluetoothServiceInfo::RfcommProtocol) {
381 errorString = QBluetoothSocket::tr("Socket type not supported");
382 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
383 return;
384 }
385
386 const QString connectionHostName = service.attribute(0xBEEF).toString();
387 const QString connectionServiceName = service.attribute(0xBEF0).toString();
388 if (service.protocolServiceMultiplexer() > 0) {
389 Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::L2capProtocol);
390
391 if (!ensureNativeSocket(QBluetoothServiceInfo::L2capProtocol)) {
392 errorString = QBluetoothSocket::tr("Unknown socket error");
393 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
394 return;
395 }
396 connectToServiceHelper(service.device().address(),
397 quint16(service.protocolServiceMultiplexer()), openMode);
398 } else if (!connectionHostName.isEmpty() && !connectionServiceName.isEmpty()) {
399 Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::RfcommProtocol);
400 if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) {
401 errorString = QBluetoothSocket::tr("Unknown socket error");
402 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
403 return;
404 }
405 HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(connectionHostName.utf16()));
406 ComPtr<IHostNameFactory> hostNameFactory;
407 HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
408 &hostNameFactory);
409 Q_ASSERT_SUCCEEDED(hr);
410 ComPtr<IHostName> remoteHost;
411 hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
412 connectToService(remoteHost, connectionServiceName, openMode);
413 } else if (service.serverChannel() > 0) {
414 Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::RfcommProtocol);
415
416 if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) {
417 errorString = QBluetoothSocket::tr("Unknown socket error");
418 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
419 return;
420 }
421 connectToServiceHelper(service.device().address(), quint16(service.serverChannel()),
422 openMode);
423 } else {
424 // try doing service discovery to see if we can find the socket
425 if (service.serviceUuid().isNull()
426 && !service.serviceClassUuids().contains(QBluetoothUuid::ServiceClassUuid::SerialPort)) {
427 qCWarning(QT_BT_WINDOWS) << "No port, no PSM, and no UUID provided. Unable to connect";
428 return;
429 }
430 qCDebug(QT_BT_WINDOWS) << "Need a port/psm, doing discovery";
431 q->doDeviceDiscovery(service, openMode);
432 }
433}
434
435void QBluetoothSocketPrivateWinRT::connectToService(
436 const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode)
437{
438 Q_Q(QBluetoothSocket);
439
440 if (q->state() != QBluetoothSocket::SocketState::UnconnectedState) {
441 qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWinRT::connectToService called on busy socket";
442 errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
443 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
444 return;
445 }
446
447 if (q->socketType() != QBluetoothServiceInfo::RfcommProtocol) {
448 errorString = QBluetoothSocket::tr("Socket type not supported");
449 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
450 return;
451 }
452
453 QBluetoothServiceInfo service;
454 QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::MiscellaneousDevice);
455 service.setDevice(device);
456 service.setServiceUuid(uuid);
457 q->doDeviceDiscovery(service, openMode);
458}
459
460void QBluetoothSocketPrivateWinRT::connectToService(
461 const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
462{
463 Q_Q(QBluetoothSocket);
464
465 if (q->state() != QBluetoothSocket::SocketState::UnconnectedState) {
466 qCWarning(QT_BT_WINDOWS) << "QBluetoothSocketPrivateWinRT::connectToService called on busy socket";
467 errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
468 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
469 return;
470 }
471
472 if (q->socketType() != QBluetoothServiceInfo::RfcommProtocol) {
473 errorString = QBluetoothSocket::tr("Socket type not supported");
474 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
475 return;
476 }
477
478 connectToServiceHelper(address, port, openMode);
479}
480
481void QBluetoothSocketPrivateWinRT::abort()
482{
483 Q_Q(QBluetoothSocket);
484 if (state == QBluetoothSocket::SocketState::UnconnectedState)
485 return;
486
487 disconnect(m_worker, &SocketWorker::newDataReceived,
488 this, &QBluetoothSocketPrivateWinRT::handleNewData);
489 disconnect(m_worker, &SocketWorker::socketErrorOccured,
490 this, &QBluetoothSocketPrivateWinRT::handleError);
491 m_worker->close();
492 m_worker->deleteLater();
493
494 if (socket != -1) {
495 m_socketObject = nullptr;
496 socket = -1;
497 }
498
499 const bool wasConnected = q->state() == QBluetoothSocket::SocketState::ConnectedState;
500 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
501 if (wasConnected) {
502 q->setOpenMode(QIODevice::NotOpen);
503 emit q->readChannelFinished();
504 }
505}
506
507QString QBluetoothSocketPrivateWinRT::localName() const
508{
509 const QBluetoothAddress address = localAddress();
510 if (address.isNull())
511 return QString();
512
513 QBluetoothLocalDevice device(address);
514 return device.name();
515}
516
517static QString fromWinApiAddress(HString address)
518{
519 // WinAPI returns address with parentheses around it. We need to remove
520 // them to convert to QBluetoothAddress.
521 QString addressStr(qt_QStringFromHString(address));
522 if (addressStr.startsWith(QLatin1Char('(')) && addressStr.endsWith(QLatin1Char(')'))) {
523 addressStr = addressStr.sliced(1, addressStr.size() - 2);
524 }
525 return addressStr;
526}
527
528QBluetoothAddress QBluetoothSocketPrivateWinRT::localAddress() const
529{
530 if (!m_socketObject)
531 return QBluetoothAddress();
532
533 HRESULT hr;
534 ComPtr<IStreamSocketInformation> info;
535 hr = m_socketObject->get_Information(&info);
536 Q_ASSERT_SUCCEEDED(hr);
537 ComPtr<IHostName> localHost;
538 hr = info->get_LocalAddress(&localHost);
539 Q_ASSERT_SUCCEEDED(hr);
540 if (localHost) {
541 HString localAddress;
542 hr = localHost->get_CanonicalName(localAddress.GetAddressOf());
543 Q_ASSERT_SUCCEEDED(hr);
544 return QBluetoothAddress(fromWinApiAddress(std::move(localAddress)));
545 }
546 return QBluetoothAddress();
547}
548
549quint16 QBluetoothSocketPrivateWinRT::localPort() const
550{
551 if (!m_socketObject)
552 return 0;
553
554 HRESULT hr;
555 ComPtr<IStreamSocketInformation> info;
556 hr = m_socketObject->get_Information(&info);
557 Q_ASSERT_SUCCEEDED(hr);
558 HString localPortString;
559 hr = info->get_LocalPort(localPortString.GetAddressOf());
560 Q_ASSERT_SUCCEEDED(hr);
561 bool ok = true;
562 const uint port = qt_QStringFromHString(localPortString).toUInt(&ok);
563 if (!ok || port > UINT16_MAX) {
564 qCWarning(QT_BT_WINDOWS) << "Unexpected local port";
565 return 0;
566 }
567 return quint16(port);
568}
569
570QString QBluetoothSocketPrivateWinRT::peerName() const
571{
572 if (!m_socketObject)
573 return QString();
574
575 HRESULT hr;
576 ComPtr<IStreamSocketInformation> info;
577 hr = m_socketObject->get_Information(&info);
578 Q_ASSERT_SUCCEEDED(hr);
579 ComPtr<IHostName> remoteHost;
580 hr = info->get_RemoteHostName(&remoteHost);
581 Q_ASSERT_SUCCEEDED(hr);
582 if (remoteHost) {
583 HString remoteHostName;
584 hr = remoteHost->get_DisplayName(remoteHostName.GetAddressOf());
585 Q_ASSERT_SUCCEEDED(hr);
586 return qt_QStringFromHString(remoteHostName);
587 }
588 return {};
589}
590
591QBluetoothAddress QBluetoothSocketPrivateWinRT::peerAddress() const
592{
593 if (!m_socketObject)
594 return QBluetoothAddress();
595
596 HRESULT hr;
597 ComPtr<IStreamSocketInformation> info;
598 hr = m_socketObject->get_Information(&info);
599 Q_ASSERT_SUCCEEDED(hr);
600 ComPtr<IHostName> remoteHost;
601 hr = info->get_RemoteAddress(&remoteHost);
602 Q_ASSERT_SUCCEEDED(hr);
603 if (remoteHost) {
604 HString remoteAddress;
605 hr = remoteHost->get_CanonicalName(remoteAddress.GetAddressOf());
606 Q_ASSERT_SUCCEEDED(hr);
607 return QBluetoothAddress(fromWinApiAddress(std::move(remoteAddress)));
608 }
609 return QBluetoothAddress();
610}
611
612quint16 QBluetoothSocketPrivateWinRT::peerPort() const
613{
614 if (!m_socketObject)
615 return 0;
616
617 HRESULT hr;
618 ComPtr<IStreamSocketInformation> info;
619 hr = m_socketObject->get_Information(&info);
620 Q_ASSERT_SUCCEEDED(hr);
621 HString remotePortString;
622 hr = info->get_RemotePort(remotePortString.GetAddressOf());
623 Q_ASSERT_SUCCEEDED(hr);
624 bool ok = true;
625 const uint port = qt_QStringFromHString(remotePortString).toUInt(&ok);
626 if (!ok || port > UINT16_MAX) {
627 qCWarning(QT_BT_WINDOWS) << "Unexpected remote port";
628 return 0;
629 }
630 return quint16(port);
631}
632
633qint64 QBluetoothSocketPrivateWinRT::writeData(const char *data, qint64 maxSize)
634{
635 Q_Q(QBluetoothSocket);
636
637 if (state != QBluetoothSocket::SocketState::ConnectedState) {
638 errorString = QBluetoothSocket::tr("Cannot write while not connected");
639 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
640 return -1;
641 }
642
643 ComPtr<IOutputStream> stream;
644 HRESULT hr;
645 hr = m_socketObject->get_OutputStream(&stream);
646 Q_ASSERT_SUCCEEDED(hr);
647
648 qint64 bytesWritten = writeIOStream(stream, data, maxSize);
649 if (bytesWritten < 0) {
650 qCWarning(QT_BT_WINDOWS) << "Socket::writeData: " << state;
651 errorString = QBluetoothSocket::tr("Cannot read while not connected");
652 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
653 }
654
655 emit q->bytesWritten(bytesWritten);
656 return bytesWritten;
657}
658
659qint64 QBluetoothSocketPrivateWinRT::readData(char *data, qint64 maxSize)
660{
661 Q_Q(QBluetoothSocket);
662
663 if (state != QBluetoothSocket::SocketState::ConnectedState) {
664 errorString = QBluetoothSocket::tr("Cannot read while not connected");
665 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
666 return -1;
667 }
668
669 if (!rxBuffer.isEmpty()) {
670 if (maxSize > INT_MAX)
671 maxSize = INT_MAX;
672 return rxBuffer.read(data, int(maxSize));
673 }
674
675 return 0;
676}
677
678void QBluetoothSocketPrivateWinRT::close()
679{
680 abort();
681}
682
683bool QBluetoothSocketPrivateWinRT::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
684 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
685{
686 Q_UNUSED(socketDescriptor);
687 Q_UNUSED(socketType);
688 Q_UNUSED(socketState);
689 Q_UNUSED(openMode);
690 qCWarning(QT_BT_WINDOWS) << "No socket descriptor support on WinRT.";
691 return false;
692}
693
694bool QBluetoothSocketPrivateWinRT::setSocketDescriptor(ComPtr<IStreamSocket> socketPtr, QBluetoothServiceInfo::Protocol socketType,
695 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
696{
697 Q_Q(QBluetoothSocket);
698 if (socketType != QBluetoothServiceInfo::RfcommProtocol || !socketPtr)
699 return false;
700
701 m_socketObject = socketPtr;
702 socket = qintptr(m_socketObject.Get());
703 m_worker->setSocket(m_socketObject);
704 q->setSocketState(socketState);
705 if (socketState == QBluetoothSocket::SocketState::ConnectedState)
706 m_worker->startReading();
707 // QBluetoothSockets are unbuffered on Windows
708 q->setOpenMode(openMode | QIODevice::Unbuffered);
709 return true;
710}
711
712qint64 QBluetoothSocketPrivateWinRT::bytesAvailable() const
713{
714 return rxBuffer.size();
715}
716
717qint64 QBluetoothSocketPrivateWinRT::bytesToWrite() const
718{
719 return 0; // nothing because always unbuffered
720}
721
722bool QBluetoothSocketPrivateWinRT::canReadLine() const
723{
724 return rxBuffer.canReadLine();
725}
726
727void QBluetoothSocketPrivateWinRT::handleNewData(const QList<QByteArray> &data)
728{
729 // Defer putting the data into the list until the next event loop iteration
730 // (where the readyRead signal is emitted as well)
731 QMetaObject::invokeMethod(this, "addToPendingData", Qt::QueuedConnection,
732 Q_ARG(QList<QByteArray>, data));
733}
734
735void QBluetoothSocketPrivateWinRT::handleError(QBluetoothSocket::SocketError error)
736{
737 Q_Q(QBluetoothSocket);
738 switch (error) {
739 case QBluetoothSocket::SocketError::NetworkError:
740 errorString = QBluetoothSocket::tr("Network error");
741 break;
742 case QBluetoothSocket::SocketError::RemoteHostClosedError:
743 errorString = QBluetoothSocket::tr("Remote host closed connection");
744 break;
745 default:
746 errorString = QBluetoothSocket::tr("Unknown socket error");
747 }
748
749 q->setSocketError(error);
750 const bool wasConnected = q->state() == QBluetoothSocket::SocketState::ConnectedState;
751 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
752 if (wasConnected) {
753 q->setOpenMode(QIODevice::NotOpen);
754 emit q->readChannelFinished();
755 }
756}
757
758void QBluetoothSocketPrivateWinRT::addToPendingData(const QList<QByteArray> &data)
759{
760 Q_Q(QBluetoothSocket);
761 for (const QByteArray &newData : data) {
762 char *writePointer = rxBuffer.reserve(newData.length());
763 memcpy(writePointer, newData.data(), size_t(newData.length()));
764 }
765 emit q->readyRead();
766}
767
768HRESULT QBluetoothSocketPrivateWinRT::handleConnectOpFinished(ABI::Windows::Foundation::IAsyncAction *action, ABI::Windows::Foundation::AsyncStatus status)
769{
770 Q_Q(QBluetoothSocket);
771 if (status != Completed || !m_connectOp) { // Protect against a late callback
772 errorString = QBluetoothSocket::tr("Unknown socket error");
773 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
774 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
775 return S_OK;
776 }
777
778 HRESULT hr = action->GetResults();
779 switch (hr) {
780
781 // A connection attempt failed because the connected party did not properly respond after a
782 // period of time, or established connection failed because connected host has failed to respond.
783 case HRESULT_FROM_WIN32(WSAETIMEDOUT):
784 errorString = QBluetoothSocket::tr("Connection timed out");
785 q->setSocketError(QBluetoothSocket::SocketError::NetworkError);
786 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
787 return S_OK;
788 // A socket operation was attempted to an unreachable host.
789 case HRESULT_FROM_WIN32(WSAEHOSTUNREACH):
790 errorString = QBluetoothSocket::tr("Host not reachable");
791 q->setSocketError(QBluetoothSocket::SocketError::HostNotFoundError);
792 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
793 return S_OK;
794 // No connection could be made because the target machine actively refused it.
795 case HRESULT_FROM_WIN32(WSAECONNREFUSED):
796 errorString = QBluetoothSocket::tr("Host refused connection");
797 q->setSocketError(QBluetoothSocket::SocketError::HostNotFoundError);
798 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
799 return S_OK;
800 default:
801 if (FAILED(hr)) {
802 errorString = QBluetoothSocket::tr("Unknown socket error");
803 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
804 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
805 return S_OK;
806 }
807 }
808
809 // The callback might be triggered several times if we do not cancel/reset it here
810 if (m_connectOp) {
811 ComPtr<IAsyncInfo> info;
812 hr = m_connectOp.As(&info);
813 Q_ASSERT_SUCCEEDED(hr);
814 if (info) {
815 hr = info->Cancel();
816 Q_ASSERT_SUCCEEDED(hr);
817 hr = info->Close();
818 Q_ASSERT_SUCCEEDED(hr);
819 }
820 m_connectOp.Reset();
821 }
822
823 // QBluetoothSockets are unbuffered on Windows
824 q->setOpenMode(requestedOpenMode | QIODevice::Unbuffered);
825 q->setSocketState(QBluetoothSocket::SocketState::ConnectedState);
826 m_worker->startReading();
827
828 return S_OK;
829}
830
831QT_END_NAMESPACE
832
833#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 mainThreadCoInit(void *caller)
void mainThreadCoUninit(void *caller)
QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcEventDispatcher)
ComPtr< IBufferFactory > bufferFactory