Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtBluetooth module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qbluetoothsocket_winrt_p.h"
41
42#ifdef CLASSIC_APP_BUILD
43#define Q_OS_WINRT
44#endif
45#include <qfunctions_winrt.h>
46
47#include <private/qeventdispatcher_winrt_p.h>
48
49#include <QtBluetooth/QBluetoothLocalDevice>
50#include <QtBluetooth/qbluetoothdeviceinfo.h>
51#include <QtBluetooth/qbluetoothserviceinfo.h>
52#include <QtCore/qloggingcategory.h>
53
54#include <robuffer.h>
55#include <windows.devices.bluetooth.h>
56#include <windows.networking.sockets.h>
57#include <windows.storage.streams.h>
58#include <wrl.h>
59
60using namespace Microsoft::WRL;
61using namespace Microsoft::WRL::Wrappers;
62using namespace ABI::Windows::Devices::Bluetooth;
63using namespace ABI::Windows::Devices::Bluetooth::Rfcomm;
64using namespace ABI::Windows::Foundation;
65using namespace ABI::Windows::Foundation::Collections;
66using namespace ABI::Windows::Networking;
67using namespace ABI::Windows::Networking::Sockets;
68using namespace ABI::Windows::Storage::Streams;
69
70typedef IAsyncOperationWithProgressCompletedHandler<IBuffer *, UINT32> SocketReadCompletedHandler;
71typedef IAsyncOperationWithProgress<IBuffer *, UINT32> IAsyncBufferOperation;
72
73QT_BEGIN_NAMESPACE
74
75Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINRT)
76
77struct SocketGlobal
78{
79 SocketGlobal()
80 {
81 HRESULT hr;
82 hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
83 &bufferFactory);
84 Q_ASSERT_SUCCEEDED(hr);
85 }
86
87 ComPtr<IBufferFactory> bufferFactory;
88};
89Q_GLOBAL_STATIC(SocketGlobal, g)
90
91#define READ_BUFFER_SIZE 65536
92
93static inline QString qt_QStringFromHString(const HString &string)
94{
95 UINT32 length;
96 PCWSTR rawString = string.GetRawBuffer(&length);
97 if (length > INT_MAX)
98 length = INT_MAX;
99 return QString::fromWCharArray(rawString, int(length));
100}
101
102static qint64 writeIOStream(ComPtr<IOutputStream> stream, const char *data, qint64 len)
103{
104 ComPtr<IBuffer> buffer;
105 if (len > UINT32_MAX) {
106 qCWarning(QT_BT_WINRT) << "writeIOStream can only write up to" << UINT32_MAX << "bytes.";
107 len = UINT32_MAX;
108 }
109 quint32 ulen = static_cast<quint32>(len);
110 HRESULT hr = g->bufferFactory->Create(ulen, &buffer);
111 Q_ASSERT_SUCCEEDED(hr);
112 hr = buffer->put_Length(ulen);
113 Q_ASSERT_SUCCEEDED(hr);
114 ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
115 hr = buffer.As(&byteArrayAccess);
116 Q_ASSERT_SUCCEEDED(hr);
117 byte *bytes;
118 hr = byteArrayAccess->Buffer(&bytes);
119 Q_ASSERT_SUCCEEDED(hr);
120 memcpy(bytes, data, ulen);
121 ComPtr<IAsyncOperationWithProgress<UINT32, UINT32>> op;
122 hr = stream->WriteAsync(buffer.Get(), &op);
123 RETURN_IF_FAILED("Failed to write to stream", return -1);
124 UINT32 bytesWritten;
125 hr = QWinRTFunctions::await(op, &bytesWritten);
126 RETURN_IF_FAILED("Failed to write to stream", return -1);
127 return bytesWritten;
128}
129
130class SocketWorker : public QObject
131{
132 Q_OBJECT
133public:
134 SocketWorker()
135 {
136 }
137
138 ~SocketWorker()
139 {
140 }
141 void close()
142 {
143 m_shuttingDown = true;
144 if (Q_UNLIKELY(m_initialReadOp)) {
145 onReadyRead(m_initialReadOp.Get(), Canceled);
146 ComPtr<IAsyncInfo> info;
147 HRESULT hr = m_initialReadOp.As(&info);
148 Q_ASSERT_SUCCEEDED(hr);
149 if (info) {
150 hr = info->Cancel();
151 Q_ASSERT_SUCCEEDED(hr);
152 hr = info->Close();
153 Q_ASSERT_SUCCEEDED(hr);
154 }
155 m_initialReadOp.Reset();
156 }
157
158 if (m_readOp) {
159 onReadyRead(m_readOp.Get(), Canceled);
160 ComPtr<IAsyncInfo> info;
161 HRESULT hr = m_readOp.As(&info);
162 Q_ASSERT_SUCCEEDED(hr);
163 if (info) {
164 hr = info->Cancel();
165 Q_ASSERT_SUCCEEDED(hr);
166 hr = info->Close();
167 Q_ASSERT_SUCCEEDED(hr);
168 }
169 m_readOp.Reset();
170 }
171 }
172
173signals:
174 void newDataReceived(const QVector<QByteArray> &data);
175 void socketErrorOccured(QBluetoothSocket::SocketError error);
176
177public slots:
178 Q_INVOKABLE void notifyAboutNewData()
179 {
180 QMutexLocker locker(&m_mutex);
181 const QVector<QByteArray> newData = std::move(m_pendingData);
182 m_pendingData.clear();
183 emit newDataReceived(newData);
184 }
185
186public:
187 void startReading()
188 {
189 HRESULT hr;
190 hr = QEventDispatcherWinRT::runOnXamlThread([this]()
191 {
192 ComPtr<IBuffer> buffer;
193 HRESULT hr = g->bufferFactory->Create(READ_BUFFER_SIZE, &buffer);
194 Q_ASSERT_SUCCEEDED(hr);
195 ComPtr<IInputStream> stream;
196 hr = m_socket->get_InputStream(&stream);
197 Q_ASSERT_SUCCEEDED(hr);
198 hr = stream->ReadAsync(buffer.Get(), READ_BUFFER_SIZE, InputStreamOptions_Partial, m_initialReadOp.GetAddressOf());
199 Q_ASSERT_SUCCEEDED(hr);
200 hr = m_initialReadOp->put_Completed(Callback<SocketReadCompletedHandler>(this, &SocketWorker::onReadyRead).Get());
201 Q_ASSERT_SUCCEEDED(hr);
202 return S_OK;
203 });
204 Q_ASSERT_SUCCEEDED(hr);
205 }
206
207 HRESULT onReadyRead(IAsyncBufferOperation *asyncInfo, AsyncStatus status)
208 {
209 if (m_shuttingDown)
210 return S_OK;
211
212 if (asyncInfo == m_initialReadOp.Get()) {
213 m_initialReadOp.Reset();
214 } else if (asyncInfo == m_readOp.Get()) {
215 m_readOp.Reset();
216 } else {
217 Q_ASSERT(false);
218 }
219
220 // A read in UnconnectedState will close the socket and return -1 and thus tell the caller,
221 // that the connection was closed. The socket cannot be closed here, as the subsequent read
222 // might fail then.
223 if (status == Error || status == Canceled) {
224 emit socketErrorOccured(QBluetoothSocket::NetworkError);
225 return S_OK;
226 }
227
228 ComPtr<IBuffer> buffer;
229 HRESULT hr = asyncInfo->GetResults(&buffer);
230 if (FAILED(hr)) {
231 qErrnoWarning(hr, "Failed to get read results buffer");
232 emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
233 return S_OK;
234 }
235
236 UINT32 bufferLength;
237 hr = buffer->get_Length(&bufferLength);
238 if (FAILED(hr)) {
239 qErrnoWarning(hr, "Failed to get buffer length");
240 emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
241 return S_OK;
242 }
243 // A zero sized buffer length signals, that the remote host closed the connection. The socket
244 // cannot be closed though, as the following read might have socket descriptor -1 and thus and
245 // the closing of the socket won't be communicated to the caller. So only the error is set. The
246 // actual socket close happens inside of read.
247 if (!bufferLength) {
248 emit socketErrorOccured(QBluetoothSocket::RemoteHostClosedError);
249 return S_OK;
250 }
251
252 ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
253 hr = buffer.As(&byteArrayAccess);
254 if (FAILED(hr)) {
255 qErrnoWarning(hr, "Failed to get cast buffer");
256 emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
257 return S_OK;
258 }
259 byte *data;
260 hr = byteArrayAccess->Buffer(&data);
261 if (FAILED(hr)) {
262 qErrnoWarning(hr, "Failed to access buffer data");
263 emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
264 return S_OK;
265 }
266
267 QByteArray newData(reinterpret_cast<const char*>(data), int(bufferLength));
268 QMutexLocker readLocker(&m_mutex);
269 if (m_pendingData.isEmpty())
270 QMetaObject::invokeMethod(this, "notifyAboutNewData", Qt::QueuedConnection);
271 m_pendingData << newData;
272 readLocker.unlock();
273
274 hr = QEventDispatcherWinRT::runOnXamlThread([buffer, this]() {
275 UINT32 readBufferLength;
276 ComPtr<IInputStream> stream;
277 HRESULT hr = m_socket->get_InputStream(&stream);
278 if (FAILED(hr)) {
279 qErrnoWarning(hr, "Failed to obtain input stream");
280 emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
281 return S_OK;
282 }
283
284 // Reuse the stream buffer
285 hr = buffer->get_Capacity(&readBufferLength);
286 if (FAILED(hr)) {
287 qErrnoWarning(hr, "Failed to get buffer capacity");
288 emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
289 return S_OK;
290 }
291 hr = buffer->put_Length(0);
292 if (FAILED(hr)) {
293 qErrnoWarning(hr, "Failed to set buffer length");
294 emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
295 return S_OK;
296 }
297
298 hr = stream->ReadAsync(buffer.Get(), readBufferLength, InputStreamOptions_Partial, &m_readOp);
299 if (FAILED(hr)) {
300 qErrnoWarning(hr, "onReadyRead(): Could not read into socket stream buffer.");
301 emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
302 return S_OK;
303 }
304 hr = m_readOp->put_Completed(Callback<SocketReadCompletedHandler>(this, &SocketWorker::onReadyRead).Get());
305 if (FAILED(hr)) {
306 qErrnoWarning(hr, "onReadyRead(): Failed to set socket read callback.");
307 emit socketErrorOccured(QBluetoothSocket::UnknownSocketError);
308 return S_OK;
309 }
310 return S_OK;
311 });
312 Q_ASSERT_SUCCEEDED(hr);
313 return S_OK;
314 }
315
316 void setSocket(ComPtr<IStreamSocket> socket) { m_socket = socket; }
317
318private:
319 ComPtr<IStreamSocket> m_socket;
320 QVector<QByteArray> m_pendingData;
321 bool m_shuttingDown = false;
322
323 // Protects pendingData/pendingDatagrams which are accessed from native callbacks
324 QMutex m_mutex;
325
326 ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> m_initialReadOp;
327 ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> m_readOp;
328};
329
330QBluetoothSocketPrivateWinRT::QBluetoothSocketPrivateWinRT()
331 : m_worker(new SocketWorker())
332{
333 secFlags = QBluetooth::NoSecurity;
334 connect(m_worker, &SocketWorker::newDataReceived,
335 this, &QBluetoothSocketPrivateWinRT::handleNewData, Qt::QueuedConnection);
336 connect(m_worker, &SocketWorker::socketErrorOccured,
337 this, &QBluetoothSocketPrivateWinRT::handleError, Qt::QueuedConnection);
338}
339
340QBluetoothSocketPrivateWinRT::~QBluetoothSocketPrivateWinRT()
341{
342 abort();
343}
344
345bool QBluetoothSocketPrivateWinRT::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
346{
347 if (socket != -1) {
348 if (type == socketType)
349 return true;
350 m_socketObject = nullptr;
351 socket = -1;
352 }
353 socketType = type;
354 if (socketType != QBluetoothServiceInfo::RfcommProtocol)
355 return false;
356
357 HRESULT hr;
358 hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocket).Get(), &m_socketObject);
359 if (FAILED(hr) || !m_socketObject) {
360 qErrnoWarning(hr, "ensureNativeSocket: Could not create socket instance");
361 return false;
362 }
363 socket = qintptr(m_socketObject.Get());
364 m_worker->setSocket(m_socketObject);
365
366 return true;
367}
368
369void QBluetoothSocketPrivateWinRT::connectToService(Microsoft::WRL::ComPtr<IHostName> hostName,
370 const QString &serviceName,
371 QIODevice::OpenMode openMode)
372{
373 Q_Q(QBluetoothSocket);
374
375 if (socket == -1 && !ensureNativeSocket(socketType)) {
376 errorString = QBluetoothSocket::tr("Unknown socket error");
377 q->setSocketError(QBluetoothSocket::UnknownSocketError);
378 return;
379 }
380
381 HStringReference serviceNameReference(reinterpret_cast<LPCWSTR>(serviceName.utf16()));
382
383 HRESULT hr = m_socketObject->ConnectAsync(hostName.Get(), serviceNameReference.Get(), &m_connectOp);
384 if (hr == E_ACCESSDENIED) {
385 qErrnoWarning(hr, "QBluetoothSocketPrivateWinRT::connectToService: Unable to connect to bluetooth socket."
386 "Please check your manifest capabilities.");
387 q->setSocketState(QBluetoothSocket::UnconnectedState);
388 return;
389 }
390 Q_ASSERT_SUCCEEDED(hr);
391
392 q->setSocketState(QBluetoothSocket::ConnectingState);
393 requestedOpenMode = openMode;
394 hr = QEventDispatcherWinRT::runOnXamlThread([this]() {
395 HRESULT hr;
396 hr = m_connectOp->put_Completed(Callback<IAsyncActionCompletedHandler>(
397 this, &QBluetoothSocketPrivateWinRT::handleConnectOpFinished).Get());
398 RETURN_HR_IF_FAILED("connectToHostByName: Could not register \"connectOp\" callback");
399 return S_OK;
400 });
401 Q_ASSERT_SUCCEEDED(hr);
402}
403
404void QBluetoothSocketPrivateWinRT::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
405{
406 Q_Q(QBluetoothSocket);
407
408 if (socket == -1 && !ensureNativeSocket(socketType)) {
409 errorString = QBluetoothSocket::tr("Unknown socket error");
410 q->setSocketError(QBluetoothSocket::UnknownSocketError);
411 return;
412 }
413
414 const QString addressString = address.toString();
415 HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(addressString.utf16()));
416 ComPtr<IHostNameFactory> hostNameFactory;
417 HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
418 &hostNameFactory);
419 Q_ASSERT_SUCCEEDED(hr);
420 ComPtr<IHostName> remoteHost;
421 hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
422 RETURN_VOID_IF_FAILED("QBluetoothSocketPrivateWinRT::connectToService: Could not create hostname.");
423 const QString portString = QString::number(port);
424 connectToService(remoteHost, portString, openMode);
425}
426
427void QBluetoothSocketPrivateWinRT::connectToService(
428 const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
429{
430 Q_Q(QBluetoothSocket);
431
432 if (q->state() != QBluetoothSocket::UnconnectedState
433 && q->state() != QBluetoothSocket::ServiceLookupState) {
434 qCWarning(QT_BT_WINRT) << "QBluetoothSocket::connectToService called on busy socket";
435 errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
436 q->setSocketError(QBluetoothSocket::OperationError);
437 return;
438 }
439
440 // we are checking the service protocol and not socketType()
441 // socketType will change in ensureNativeSocket()
442 if (service.socketProtocol() != QBluetoothServiceInfo::RfcommProtocol) {
443 errorString = QBluetoothSocket::tr("Socket type not supported");
444 q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
445 return;
446 }
447
448 const QString connectionHostName = service.attribute(0xBEEF).toString();
449 const QString connectionServiceName = service.attribute(0xBEF0).toString();
450 if (service.protocolServiceMultiplexer() > 0) {
451 Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::L2capProtocol);
452
453 if (!ensureNativeSocket(QBluetoothServiceInfo::L2capProtocol)) {
454 errorString = QBluetoothSocket::tr("Unknown socket error");
455 q->setSocketError(QBluetoothSocket::UnknownSocketError);
456 return;
457 }
458 connectToServiceHelper(service.device().address(),
459 quint16(service.protocolServiceMultiplexer()), openMode);
460 } else if (!connectionHostName.isEmpty() && !connectionServiceName.isEmpty()) {
461 Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::RfcommProtocol);
462 if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) {
463 errorString = QBluetoothSocket::tr("Unknown socket error");
464 q->setSocketError(QBluetoothSocket::UnknownSocketError);
465 return;
466 }
467 HStringReference hostNameRef(reinterpret_cast<LPCWSTR>(connectionHostName.utf16()));
468 ComPtr<IHostNameFactory> hostNameFactory;
469 HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(),
470 &hostNameFactory);
471 Q_ASSERT_SUCCEEDED(hr);
472 ComPtr<IHostName> remoteHost;
473 hr = hostNameFactory->CreateHostName(hostNameRef.Get(), &remoteHost);
474 connectToService(remoteHost, connectionServiceName, openMode);
475 } else if (service.serverChannel() > 0) {
476 Q_ASSERT(service.socketProtocol() == QBluetoothServiceInfo::RfcommProtocol);
477
478 if (!ensureNativeSocket(QBluetoothServiceInfo::RfcommProtocol)) {
479 errorString = QBluetoothSocket::tr("Unknown socket error");
480 q->setSocketError(QBluetoothSocket::UnknownSocketError);
481 return;
482 }
483 connectToServiceHelper(service.device().address(), quint16(service.serverChannel()),
484 openMode);
485 } else {
486 // try doing service discovery to see if we can find the socket
487 if (service.serviceUuid().isNull()
488 && !service.serviceClassUuids().contains(QBluetoothUuid::SerialPort)) {
489 qCWarning(QT_BT_WINRT) << "No port, no PSM, and no UUID provided. Unable to connect";
490 return;
491 }
492 qCDebug(QT_BT_WINRT) << "Need a port/psm, doing discovery";
493 q->doDeviceDiscovery(service, openMode);
494 }
495}
496
497void QBluetoothSocketPrivateWinRT::connectToService(
498 const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode)
499{
500 Q_Q(QBluetoothSocket);
501
502 if (q->state() != QBluetoothSocket::UnconnectedState) {
503 qCWarning(QT_BT_WINRT) << "QBluetoothSocketPrivateWinRT::connectToService called on busy socket";
504 errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
505 q->setSocketError(QBluetoothSocket::OperationError);
506 return;
507 }
508
509 if (q->socketType() != QBluetoothServiceInfo::RfcommProtocol) {
510 errorString = QBluetoothSocket::tr("Socket type not supported");
511 q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
512 return;
513 }
514
515 QBluetoothServiceInfo service;
516 QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::MiscellaneousDevice);
517 service.setDevice(device);
518 service.setServiceUuid(uuid);
519 q->doDeviceDiscovery(service, openMode);
520}
521
522void QBluetoothSocketPrivateWinRT::connectToService(
523 const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
524{
525 Q_Q(QBluetoothSocket);
526
527 if (q->state() != QBluetoothSocket::UnconnectedState) {
528 qCWarning(QT_BT_WINRT) << "QBluetoothSocketPrivateWinRT::connectToService called on busy socket";
529 errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
530 q->setSocketError(QBluetoothSocket::OperationError);
531 return;
532 }
533
534 if (q->socketType() != QBluetoothServiceInfo::RfcommProtocol) {
535 errorString = QBluetoothSocket::tr("Socket type not supported");
536 q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
537 return;
538 }
539
540 connectToServiceHelper(address, port, openMode);
541}
542
543void QBluetoothSocketPrivateWinRT::abort()
544{
545 Q_Q(QBluetoothSocket);
546 if (state == QBluetoothSocket::UnconnectedState)
547 return;
548
549 disconnect(m_worker, &SocketWorker::newDataReceived,
550 this, &QBluetoothSocketPrivateWinRT::handleNewData);
551 disconnect(m_worker, &SocketWorker::socketErrorOccured,
552 this, &QBluetoothSocketPrivateWinRT::handleError);
553 m_worker->close();
554 m_worker->deleteLater();
555
556 if (socket != -1) {
557 m_socketObject = nullptr;
558 socket = -1;
559 }
560
561 const bool wasConnected = q->state() == QBluetoothSocket::ConnectedState;
562 q->setSocketState(QBluetoothSocket::UnconnectedState);
563 if (wasConnected) {
564 q->setOpenMode(QIODevice::NotOpen);
565 emit q->readChannelFinished();
566 }
567}
568
569QString QBluetoothSocketPrivateWinRT::localName() const
570{
571 const QBluetoothAddress address = localAddress();
572 if (address.isNull())
573 return QString();
574
575 QBluetoothLocalDevice device(address);
576 return device.name();
577}
578
579QBluetoothAddress QBluetoothSocketPrivateWinRT::localAddress() const
580{
581 if (!m_socketObject)
582 return QBluetoothAddress();
583
584 HRESULT hr;
585 ComPtr<IStreamSocketInformation> info;
586 hr = m_socketObject->get_Information(&info);
587 Q_ASSERT_SUCCEEDED(hr);
588 ComPtr<IHostName> localHost;
589 hr = info->get_LocalAddress(&localHost);
590 Q_ASSERT_SUCCEEDED(hr);
591 HString localAddress;
592 hr = localHost->get_CanonicalName(localAddress.GetAddressOf());
593 Q_ASSERT_SUCCEEDED(hr);
594 return QBluetoothAddress(qt_QStringFromHString(localAddress));
595}
596
597quint16 QBluetoothSocketPrivateWinRT::localPort() const
598{
599 if (!m_socketObject)
600 return 0;
601
602 HRESULT hr;
603 ComPtr<IStreamSocketInformation> info;
604 hr = m_socketObject->get_Information(&info);
605 Q_ASSERT_SUCCEEDED(hr);
606 HString localPortString;
607 hr = info->get_LocalPort(localPortString.GetAddressOf());
608 Q_ASSERT_SUCCEEDED(hr);
609 bool ok = true;
610 const uint port = qt_QStringFromHString(localPortString).toUInt(&ok);
611 if (!ok || port > UINT16_MAX) {
612 qCWarning(QT_BT_WINRT) << "Unexpected local port";
613 return 0;
614 }
615 return quint16(port);
616}
617
618QString QBluetoothSocketPrivateWinRT::peerName() const
619{
620 if (!m_socketObject)
621 return QString();
622
623 HRESULT hr;
624 ComPtr<IStreamSocketInformation> info;
625 hr = m_socketObject->get_Information(&info);
626 Q_ASSERT_SUCCEEDED(hr);
627 ComPtr<IHostName> remoteHost;
628 hr = info->get_RemoteHostName(&remoteHost);
629 Q_ASSERT_SUCCEEDED(hr);
630 HString remoteHostName;
631 hr = remoteHost->get_DisplayName(remoteHostName.GetAddressOf());
632 Q_ASSERT_SUCCEEDED(hr);
633 return qt_QStringFromHString(remoteHostName);
634}
635
636QBluetoothAddress QBluetoothSocketPrivateWinRT::peerAddress() const
637{
638 if (!m_socketObject)
639 return QBluetoothAddress();
640
641 HRESULT hr;
642 ComPtr<IStreamSocketInformation> info;
643 hr = m_socketObject->get_Information(&info);
644 Q_ASSERT_SUCCEEDED(hr);
645 ComPtr<IHostName> remoteHost;
646 hr = info->get_RemoteAddress(&remoteHost);
647 Q_ASSERT_SUCCEEDED(hr);
648 HString remoteAddress;
649 hr = remoteHost->get_CanonicalName(remoteAddress.GetAddressOf());
650 Q_ASSERT_SUCCEEDED(hr);
651 return QBluetoothAddress(qt_QStringFromHString(remoteAddress));
652}
653
654quint16 QBluetoothSocketPrivateWinRT::peerPort() const
655{
656 if (!m_socketObject)
657 return 0;
658
659 HRESULT hr;
660 ComPtr<IStreamSocketInformation> info;
661 hr = m_socketObject->get_Information(&info);
662 Q_ASSERT_SUCCEEDED(hr);
663 HString remotePortString;
664 hr = info->get_LocalPort(remotePortString.GetAddressOf());
665 Q_ASSERT_SUCCEEDED(hr);
666 bool ok = true;
667 const uint port = qt_QStringFromHString(remotePortString).toUInt(&ok);
668 if (!ok || port > UINT16_MAX) {
669 qCWarning(QT_BT_WINRT) << "Unexpected remote port";
670 return 0;
671 }
672 return quint16(port);
673}
674
675qint64 QBluetoothSocketPrivateWinRT::writeData(const char *data, qint64 maxSize)
676{
677 Q_Q(QBluetoothSocket);
678
679 if (state != QBluetoothSocket::ConnectedState) {
680 errorString = QBluetoothSocket::tr("Cannot write while not connected");
681 q->setSocketError(QBluetoothSocket::OperationError);
682 return -1;
683 }
684
685 ComPtr<IOutputStream> stream;
686 HRESULT hr;
687 hr = m_socketObject->get_OutputStream(&stream);
688 Q_ASSERT_SUCCEEDED(hr);
689
690 qint64 bytesWritten = writeIOStream(stream, data, maxSize);
691 if (bytesWritten < 0) {
692 qCWarning(QT_BT_WINRT) << "Socket::writeData: " << state;
693 errorString = QBluetoothSocket::tr("Cannot read while not connected");
694 q->setSocketError(QBluetoothSocket::OperationError);
695 }
696
697 emit q->bytesWritten(bytesWritten);
698 return bytesWritten;
699}
700
701qint64 QBluetoothSocketPrivateWinRT::readData(char *data, qint64 maxSize)
702{
703 Q_Q(QBluetoothSocket);
704
705 if (state != QBluetoothSocket::ConnectedState) {
706 errorString = QBluetoothSocket::tr("Cannot read while not connected");
707 q->setSocketError(QBluetoothSocket::OperationError);
708 return -1;
709 }
710
711 if (!buffer.isEmpty()) {
712 if (maxSize > INT_MAX)
713 maxSize = INT_MAX;
714 return buffer.read(data, int(maxSize));
715 }
716
717 return 0;
718}
719
720void QBluetoothSocketPrivateWinRT::close()
721{
722 abort();
723}
724
725bool QBluetoothSocketPrivateWinRT::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
726 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
727{
728 Q_UNUSED(socketDescriptor);
729 Q_UNUSED(socketType)
730 Q_UNUSED(socketState);
731 Q_UNUSED(openMode);
732 qCWarning(QT_BT_WINRT) << "No socket descriptor support on WinRT.";
733 return false;
734}
735
736bool QBluetoothSocketPrivateWinRT::setSocketDescriptor(ComPtr<IStreamSocket> socketPtr, QBluetoothServiceInfo::Protocol socketType,
737 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
738{
739 Q_Q(QBluetoothSocket);
740 if (socketType != QBluetoothServiceInfo::RfcommProtocol || !socketPtr)
741 return false;
742
743 m_socketObject = socketPtr;
744 socket = qintptr(m_socketObject.Get());
745 m_worker->setSocket(m_socketObject);
746 q->setSocketState(socketState);
747 if (socketState == QBluetoothSocket::ConnectedState)
748 m_worker->startReading();
749 q->setOpenMode(openMode);
750 return true;
751}
752
753qint64 QBluetoothSocketPrivateWinRT::bytesAvailable() const
754{
755 return buffer.size();
756}
757
758qint64 QBluetoothSocketPrivateWinRT::bytesToWrite() const
759{
760 return 0; // nothing because always unbuffered
761}
762
763bool QBluetoothSocketPrivateWinRT::canReadLine() const
764{
765 return buffer.canReadLine();
766}
767
768void QBluetoothSocketPrivateWinRT::handleNewData(const QVector<QByteArray> &data)
769{
770 // Defer putting the data into the list until the next event loop iteration
771 // (where the readyRead signal is emitted as well)
772 QMetaObject::invokeMethod(this, "addToPendingData", Qt::QueuedConnection,
773 Q_ARG(QVector<QByteArray>, data));
774}
775
776void QBluetoothSocketPrivateWinRT::handleError(QBluetoothSocket::SocketError error)
777{
778 Q_Q(QBluetoothSocket);
779 switch (error) {
780 case QBluetoothSocket::NetworkError:
781 errorString = QBluetoothSocket::tr("Network error");
782 break;
783 case QBluetoothSocket::RemoteHostClosedError:
784 errorString = QBluetoothSocket::tr("Remote host closed connection");
785 break;
786 default:
787 errorString = QBluetoothSocket::tr("Unknown socket error");
788 }
789
790 q->setSocketError(error);
791 const bool wasConnected = q->state() == QBluetoothSocket::ConnectedState;
792 q->setSocketState(QBluetoothSocket::UnconnectedState);
793 if (wasConnected) {
794 q->setOpenMode(QIODevice::NotOpen);
795 emit q->readChannelFinished();
796 }
797}
798
799void QBluetoothSocketPrivateWinRT::addToPendingData(const QVector<QByteArray> &data)
800{
801 Q_Q(QBluetoothSocket);
802 QMutexLocker locker(&m_readMutex);
803 m_pendingData.append(data);
804 for (const QByteArray &newData : data) {
805 char *writePointer = buffer.reserve(newData.length());
806 memcpy(writePointer, newData.data(), size_t(newData.length()));
807 }
808 locker.unlock();
809 emit q->readyRead();
810}
811
812HRESULT QBluetoothSocketPrivateWinRT::handleConnectOpFinished(ABI::Windows::Foundation::IAsyncAction *action, ABI::Windows::Foundation::AsyncStatus status)
813{
814 Q_Q(QBluetoothSocket);
815 if (status != Completed || !m_connectOp) { // Protect against a late callback
816 errorString = QBluetoothSocket::tr("Unknown socket error");
817 q->setSocketError(QBluetoothSocket::UnknownSocketError);
818 q->setSocketState(QBluetoothSocket::UnconnectedState);
819 return S_OK;
820 }
821
822 HRESULT hr = action->GetResults();
823 switch (hr) {
824
825 // A connection attempt failed because the connected party did not properly respond after a
826 // period of time, or established connection failed because connected host has failed to respond.
827 case HRESULT_FROM_WIN32(WSAETIMEDOUT):
828 errorString = QBluetoothSocket::tr("Connection timed out");
829 q->setSocketError(QBluetoothSocket::NetworkError);
830 q->setSocketState(QBluetoothSocket::UnconnectedState);
831 return S_OK;
832 // A socket operation was attempted to an unreachable host.
833 case HRESULT_FROM_WIN32(WSAEHOSTUNREACH):
834 errorString = QBluetoothSocket::tr("Host not reachable");
835 q->setSocketError(QBluetoothSocket::HostNotFoundError);
836 q->setSocketState(QBluetoothSocket::UnconnectedState);
837 return S_OK;
838 // No connection could be made because the target machine actively refused it.
839 case HRESULT_FROM_WIN32(WSAECONNREFUSED):
840 errorString = QBluetoothSocket::tr("Host refused connection");
841 q->setSocketError(QBluetoothSocket::HostNotFoundError);
842 q->setSocketState(QBluetoothSocket::UnconnectedState);
843 return S_OK;
844 default:
845 if (FAILED(hr)) {
846 errorString = QBluetoothSocket::tr("Unknown socket error");
847 q->setSocketError(QBluetoothSocket::UnknownSocketError);
848 q->setSocketState(QBluetoothSocket::UnconnectedState);
849 return S_OK;
850 }
851 }
852
853 // The callback might be triggered several times if we do not cancel/reset it here
854 if (m_connectOp) {
855 ComPtr<IAsyncInfo> info;
856 hr = m_connectOp.As(&info);
857 Q_ASSERT_SUCCEEDED(hr);
858 if (info) {
859 hr = info->Cancel();
860 Q_ASSERT_SUCCEEDED(hr);
861 hr = info->Close();
862 Q_ASSERT_SUCCEEDED(hr);
863 }
864 m_connectOp.Reset();
865 }
866
867 q->setOpenMode(requestedOpenMode);
868 q->setSocketState(QBluetoothSocket::ConnectedState);
869 m_worker->startReading();
870
871 return S_OK;
872}
873
874QT_END_NAMESPACE
875
876#include "qbluetoothsocket_winrt.moc"
877

Warning: That file was not part of the compilation database. It may have many parsing errors.