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

1/****************************************************************************
2**
3** Copyright (C) 2016 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 "qbluetoothservicediscoveryagent.h"
41// The order is important (the first header contains
42// the base class for a private socket) - workaround for
43// dependencies problem.
44#include "qbluetoothsocketbase_p.h"
45#include "qbluetoothsocket_osx_p.h"
46
47#include "osx/osxbtrfcommchannel_p.h"
48#include "osx/osxbtl2capchannel_p.h"
49#include "qbluetoothlocaldevice.h"
50#include "qbluetoothdeviceinfo.h"
51#include "osx/osxbtutility_p.h"
52#include "osx/uistrings_p.h"
53#include "qbluetoothsocket.h"
54
55#include <QtCore/qloggingcategory.h>
56#include <QtCore/qmetaobject.h>
57
58#include <algorithm>
59#include <limits>
60
61QT_BEGIN_NAMESPACE
62
63namespace {
64
65using DarwinBluetooth::RetainPolicy;
66using ObjCL2CAPChannel = QT_MANGLE_NAMESPACE(OSXBTL2CAPChannel);
67using ObjCRFCOMMChannel = QT_MANGLE_NAMESPACE(OSXBTRFCOMMChannel);
68
69} // unnamed namespace
70
71QBluetoothSocketPrivate::QBluetoothSocketPrivate()
72 : writeChunk(std::numeric_limits<UInt16>::max())
73{
74 q_ptr = nullptr;
75}
76
77QBluetoothSocketPrivate::~QBluetoothSocketPrivate()
78{
79}
80
81bool QBluetoothSocketPrivate::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
82{
83 // For now - very simplistic, we don't call it in this file, public class
84 // only calls it in a ctor, setting the protocol RFCOMM (in case of Android)
85 // or, indeed, doing, socket-related initialization in BlueZ backend.
86 Q_ASSERT(socketType == QBluetoothServiceInfo::UnknownProtocol);
87 socketType = type;
88 return type != QBluetoothServiceInfo::UnknownProtocol;
89}
90
91QString QBluetoothSocketPrivate::localName() const
92{
93 const QBluetoothLocalDevice device;
94 return device.name();
95}
96
97QBluetoothAddress QBluetoothSocketPrivate::localAddress() const
98{
99 const QBluetoothLocalDevice device;
100 return device.address();
101}
102
103quint16 QBluetoothSocketPrivate::localPort() const
104{
105 return 0;
106}
107
108QString QBluetoothSocketPrivate::peerName() const
109{
110 QT_BT_MAC_AUTORELEASEPOOL;
111
112 NSString *nsName = nil;
113 if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
114 if (rfcommChannel)
115 nsName = [rfcommChannel.getAs<ObjCRFCOMMChannel>() peerName];
116 } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
117 if (l2capChannel)
118 nsName = [l2capChannel.getAs<ObjCL2CAPChannel>() peerName];
119 }
120
121 if (nsName)
122 return QString::fromNSString(nsName);
123
124 return QString();
125}
126
127QBluetoothAddress QBluetoothSocketPrivate::peerAddress() const
128{
129 BluetoothDeviceAddress addr = {};
130 if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
131 if (rfcommChannel)
132 addr = [rfcommChannel.getAs<ObjCRFCOMMChannel>() peerAddress];
133 } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
134 if (l2capChannel)
135 addr = [l2capChannel.getAs<ObjCL2CAPChannel>() peerAddress];
136 }
137
138 return OSXBluetooth::qt_address(&addr);
139}
140
141quint16 QBluetoothSocketPrivate::peerPort() const
142{
143 if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
144 if (rfcommChannel)
145 return [rfcommChannel.getAs<ObjCRFCOMMChannel>() getChannelID];
146 } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
147 if (l2capChannel)
148 return [l2capChannel.getAs<ObjCL2CAPChannel>() getPSM];
149 }
150
151 return 0;
152}
153
154void QBluetoothSocketPrivate::abort()
155{
156 // Can never be called while we're in connectToService:
157 Q_ASSERT_X(!isConnecting, Q_FUNC_INFO, "internal inconsistency - "
158 "still in connectToService()");
159
160 if (socketType == QBluetoothServiceInfo::RfcommProtocol)
161 rfcommChannel.reset();
162 else if (socketType == QBluetoothServiceInfo::L2capProtocol)
163 l2capChannel.reset();
164
165 Q_ASSERT(q_ptr);
166
167 q_ptr->setSocketState(QBluetoothSocket::UnconnectedState);
168 emit q_ptr->readChannelFinished();
169 emit q_ptr->disconnected();
170
171}
172
173void QBluetoothSocketPrivate::close()
174{
175 // Can never be called while we're in connectToService:
176 Q_ASSERT_X(!isConnecting, Q_FUNC_INFO, "internal inconsistency - "
177 "still in connectToService()");
178
179 if (!txBuffer.size())
180 abort();
181}
182
183
184qint64 QBluetoothSocketPrivate::writeData(const char *data, qint64 maxSize)
185{
186 Q_ASSERT_X(data, Q_FUNC_INFO, "invalid data (null)");
187 Q_ASSERT_X(maxSize > 0, Q_FUNC_INFO, "invalid data size");
188
189 if (state != QBluetoothSocket::ConnectedState) {
190 errorString = QCoreApplication::translate(SOCKET, SOC_NOWRITE);
191 q_ptr->setSocketError(QBluetoothSocket::OperationError);
192 return -1;
193 }
194
195 // We do not have a real socket API under the hood,
196 // IOBluetoothL2CAPChannel is buffered (writeAsync).
197
198 if (!txBuffer.size())
199 QMetaObject::invokeMethod(this, "_q_writeNotify", Qt::QueuedConnection);
200
201 char *dst = txBuffer.reserve(int(maxSize));
202 std::copy(data, data + maxSize, dst);
203
204 return maxSize;
205}
206
207qint64 QBluetoothSocketPrivate::readData(char *data, qint64 maxSize)
208{
209 if (!data)
210 return 0;
211
212 if (state != QBluetoothSocket::ConnectedState) {
213 errorString = QCoreApplication::translate(SOCKET, SOC_NOREAD);
214 q_ptr->setSocketError(QBluetoothSocket::OperationError);
215 return -1;
216 }
217
218 if (!buffer.isEmpty())
219 return buffer.read(data, int(maxSize));
220
221 return 0;
222}
223
224qint64 QBluetoothSocketPrivate::bytesAvailable() const
225{
226 return buffer.size();
227}
228
229bool QBluetoothSocketPrivate::canReadLine() const
230{
231 return buffer.canReadLine();
232}
233
234qint64 QBluetoothSocketPrivate::bytesToWrite() const
235{
236 return txBuffer.size();
237}
238
239bool QBluetoothSocketPrivate::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
240 QBluetoothSocket::SocketState socketState, QIODevice::OpenMode openMode)
241{
242 Q_UNUSED(socketDescriptor)
243 Q_UNUSED(socketType)
244 Q_UNUSED(socketState)
245 Q_UNUSED(openMode)
246
247 qCWarning(QT_BT_OSX) << "setting a socket descriptor is not supported by IOBluetooth";
248 // Noop on macOS.
249 return true;
250}
251
252void QBluetoothSocketPrivate::connectToServiceHelper(const QBluetoothAddress &address, quint16 port,
253 QIODevice::OpenMode openMode)
254{
255 Q_UNUSED(address)
256 Q_UNUSED(port)
257 Q_UNUSED(openMode)
258}
259
260void QBluetoothSocketPrivate::connectToService(const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
261{
262 Q_ASSERT(q_ptr);
263
264 OSXBluetooth::qt_test_iobluetooth_runloop();
265
266 if (state!= QBluetoothSocket::UnconnectedState && state != QBluetoothSocket::ServiceLookupState) {
267 qCWarning(QT_BT_OSX) << "called on a busy socket";
268 errorString = QCoreApplication::translate(SOCKET, SOC_CONNECT_IN_PROGRESS);
269 q_ptr->setSocketError(QBluetoothSocket::OperationError);
270 return;
271 }
272
273 // Report this problem early, potentially avoid device discovery:
274 if (service.socketProtocol() == QBluetoothServiceInfo::UnknownProtocol) {
275 qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type";
276 errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR);
277 q_ptr->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
278 return;
279 }
280
281 socketType = service.socketProtocol();
282
283 if (service.protocolServiceMultiplexer() > 0) {
284 connectToService(service.device().address(),
285 quint16(service.protocolServiceMultiplexer()),
286 openMode);
287 } else if (service.serverChannel() > 0) {
288 connectToService(service.device().address(),
289 quint16(service.serverChannel()),
290 openMode);
291 } else {
292 // Try service discovery.
293 if (service.serviceUuid().isNull()) {
294 qCWarning(QT_BT_OSX) << "No port, no PSM, and no "
295 "UUID provided, unable to connect";
296 return;
297 }
298
299 q_ptr->doDeviceDiscovery(service, openMode);
300 }
301}
302
303void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid,
304 QIODevice::OpenMode openMode)
305{
306 Q_ASSERT(q_ptr);
307
308 OSXBluetooth::qt_test_iobluetooth_runloop();
309
310 // Report this problem early, avoid device discovery:
311 if (socketType == QBluetoothServiceInfo::UnknownProtocol) {
312 qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type";
313 errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR);
314 q_ptr->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
315 return;
316 }
317
318 if (state != QBluetoothSocket::UnconnectedState) {
319 qCWarning(QT_BT_OSX) << "called on a busy socket";
320 errorString = QCoreApplication::translate(SOCKET, SOC_CONNECT_IN_PROGRESS);
321 q_ptr->setSocketError(QBluetoothSocket::OperationError);
322 return;
323 }
324
325 QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::MiscellaneousDevice);
326 QBluetoothServiceInfo service;
327 service.setDevice(device);
328 service.setServiceUuid(uuid);
329 q_ptr->doDeviceDiscovery(service, openMode);
330}
331
332void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, quint16 port,
333 QIODevice::OpenMode mode)
334{
335 Q_ASSERT(q_ptr);
336
337 OSXBluetooth::qt_test_iobluetooth_runloop();
338
339 if (socketType == QBluetoothServiceInfo::UnknownProtocol) {
340 qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type";
341 errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR);
342 q_ptr->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
343 return;
344 }
345
346 Q_ASSERT_X(state == QBluetoothSocket::ServiceLookupState || state == QBluetoothSocket::UnconnectedState,
347 Q_FUNC_INFO, "invalid state");
348
349 q_ptr->setOpenMode(mode);
350
351 socketError = QBluetoothSocket::NoSocketError;
352 errorString.clear();
353 buffer.clear();
354 txBuffer.clear();
355
356 IOReturn status = kIOReturnError;
357 // Setting socket state on q_ptr will emit a signal,
358 // we'd like to avoid any signal until this function completes.
359 const QBluetoothSocket::SocketState oldState = state;
360 // To prevent other connectToService calls (from QBluetoothSocket):
361 // and also avoid signals in delegate callbacks.
362 state = QBluetoothSocket::ConnectingState;
363 // We're still inside this function:
364 isConnecting = true;
365
366 // We'll later (or now) have to set an open mode on QBluetoothSocket.
367 openMode = mode;
368
369 if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
370 rfcommChannel.reset([[ObjCRFCOMMChannel alloc] initWithDelegate:this], RetainPolicy::noInitialRetain);
371 if (rfcommChannel)
372 status = [rfcommChannel.getAs<ObjCRFCOMMChannel>() connectAsyncToDevice:address withChannelID:port];
373 else
374 status = kIOReturnNoMemory;
375 } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
376 l2capChannel.reset([[ObjCL2CAPChannel alloc] initWithDelegate:this], RetainPolicy::noInitialRetain);
377 if (l2capChannel)
378 status = [l2capChannel.getAs<ObjCL2CAPChannel>() connectAsyncToDevice:address withPSM:port];
379 else
380 status = kIOReturnNoMemory;
381 }
382
383 // We're probably still connecting, but at least are leaving this function:
384 isConnecting = false;
385
386 // QBluetoothSocket will change the state and also emit
387 // a signal later if required.
388 if (status == kIOReturnSuccess && socketError == QBluetoothSocket::NoSocketError) {
389 if (state == QBluetoothSocket::ConnectedState) {
390 // Callback 'channelOpenComplete' fired before
391 // connectToService finished:
392 state = oldState;
393 // Connected, setOpenMode on a QBluetoothSocket.
394 q_ptr->setOpenMode(openMode);
395 q_ptr->setSocketState(QBluetoothSocket::ConnectedState);
396 emit q_ptr->connected();
397 if (buffer.size()) // We also have some data already ...
398 emit q_ptr->readyRead();
399 } else if (state == QBluetoothSocket::UnconnectedState) {
400 // Even if we have some data, we can not read it if
401 // state != ConnectedState.
402 buffer.clear();
403 state = oldState;
404 q_ptr->setSocketError(QBluetoothSocket::UnknownSocketError);
405 } else {
406 // No error and we're connecting ...
407 state = oldState;
408 q_ptr->setSocketState(QBluetoothSocket::ConnectingState);
409 }
410 } else {
411 state = oldState;
412 if (status != kIOReturnSuccess)
413 errorString = OSXBluetooth::qt_error_string(status);
414 q_ptr->setSocketError(QBluetoothSocket::UnknownSocketError);
415 }
416}
417
418void QBluetoothSocketPrivate::_q_writeNotify()
419{
420 Q_ASSERT_X(socketType == QBluetoothServiceInfo::L2capProtocol
421 || socketType == QBluetoothServiceInfo::RfcommProtocol,
422 Q_FUNC_INFO, "invalid socket type");
423 Q_ASSERT_X(l2capChannel || rfcommChannel, Q_FUNC_INFO,
424 "invalid socket (no open channel)");
425 Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
426
427 if (txBuffer.size()) {
428 const bool isL2CAP = socketType == QBluetoothServiceInfo::L2capProtocol;
429 writeChunk.resize(isL2CAP ? std::numeric_limits<UInt16>::max() :
430 [rfcommChannel.getAs<ObjCRFCOMMChannel>() getMTU]);
431
432 const int size = txBuffer.read(writeChunk.data(), writeChunk.size());
433 IOReturn status = kIOReturnError;
434 if (!isL2CAP)
435 status = [rfcommChannel.getAs<ObjCRFCOMMChannel>() writeAsync:writeChunk.data() length:UInt16(size)];
436 else
437 status = [l2capChannel.getAs<ObjCL2CAPChannel>() writeAsync:writeChunk.data() length:UInt16(size)];
438
439 if (status != kIOReturnSuccess) {
440 errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR);
441 q_ptr->setSocketError(QBluetoothSocket::NetworkError);
442 return;
443 } else {
444 emit q_ptr->bytesWritten(size);
445 }
446 }
447
448 if (!txBuffer.size() && state == QBluetoothSocket::ClosingState)
449 close();
450}
451
452bool QBluetoothSocketPrivate::setRFCOMChannel(void *generic)
453{
454 // A special case "constructor": on OS X we do not have a real listening socket,
455 // instead a bluetooth server "listens" for channel open notifications and
456 // creates (if asked by a user later) a "socket" object
457 // for this connection. This function initializes
458 // a "socket" from such an external channel (reported by a notification).
459 auto channel = static_cast<IOBluetoothRFCOMMChannel *>(generic);
460 // It must be a newborn socket!
461 Q_ASSERT_X(socketError == QBluetoothSocket::NoSocketError
462 && state == QBluetoothSocket::UnconnectedState && !rfcommChannel && !l2capChannel,
463 Q_FUNC_INFO, "unexpected socket state");
464
465 openMode = QIODevice::ReadWrite;
466 rfcommChannel.reset([[ObjCRFCOMMChannel alloc] initWithDelegate:this channel:channel],
467 RetainPolicy::noInitialRetain);
468 if (rfcommChannel) {// We do not handle errors, up to an external user.
469 q_ptr->setOpenMode(QIODevice::ReadWrite);
470 state = QBluetoothSocket::ConnectedState;
471 socketType = QBluetoothServiceInfo::RfcommProtocol;
472 }
473
474 return rfcommChannel;
475}
476
477bool QBluetoothSocketPrivate::setL2CAPChannel(void *generic)
478{
479 // A special case "constructor": on OS X we do not have a real listening socket,
480 // instead a bluetooth server "listens" for channel open notifications and
481 // creates (if asked by a user later) a "socket" object
482 // for this connection. This function initializes
483 // a "socket" from such an external channel (reported by a notification).
484 auto channel = static_cast<IOBluetoothL2CAPChannel *>(generic);
485
486 // It must be a newborn socket!
487 Q_ASSERT_X(socketError == QBluetoothSocket::NoSocketError
488 && state == QBluetoothSocket::UnconnectedState && !l2capChannel && !rfcommChannel,
489 Q_FUNC_INFO, "unexpected socket state");
490
491 openMode = QIODevice::ReadWrite;
492 l2capChannel.reset([[ObjCL2CAPChannel alloc] initWithDelegate:this channel:channel], RetainPolicy::noInitialRetain);
493 if (l2capChannel) {// We do not handle errors, up to an external user.
494 q_ptr->setOpenMode(QIODevice::ReadWrite);
495 state = QBluetoothSocket::ConnectedState;
496 socketType = QBluetoothServiceInfo::L2capProtocol;
497 }
498
499 return l2capChannel;
500}
501
502void QBluetoothSocketPrivate::setChannelError(IOReturn errorCode)
503{
504 Q_UNUSED(errorCode)
505
506 Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
507
508 if (isConnecting) {
509 // The delegate's method was called while we are still in
510 // connectToService ... will emit a moment later.
511 socketError = QBluetoothSocket::UnknownSocketError;
512 } else {
513 q_ptr->setSocketError(QBluetoothSocket::UnknownSocketError);
514 }
515}
516
517void QBluetoothSocketPrivate::channelOpenComplete()
518{
519 Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
520
521 if (!isConnecting) {
522 q_ptr->setSocketState(QBluetoothSocket::ConnectedState);
523 q_ptr->setOpenMode(openMode);
524 emit q_ptr->connected();
525 } else {
526 state = QBluetoothSocket::ConnectedState;
527 // We are still in connectToService, it'll care
528 // about signals!
529 }
530}
531
532void QBluetoothSocketPrivate::channelClosed()
533{
534 Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
535
536 // Channel was closed by IOBluetooth and we can not write any data
537 // (thus close/abort probably will not work).
538
539 if (!isConnecting) {
540 q_ptr->setSocketState(QBluetoothSocket::UnconnectedState);
541 q_ptr->setOpenMode(QIODevice::NotOpen);
542 emit q_ptr->readChannelFinished();
543 emit q_ptr->disconnected();
544 } else {
545 state = QBluetoothSocket::UnconnectedState;
546 // We are still in connectToService and do not want
547 // to emit any signals yet.
548 }
549}
550
551void QBluetoothSocketPrivate::readChannelData(void *data, std::size_t size)
552{
553 Q_ASSERT_X(data, Q_FUNC_INFO, "invalid data (null)");
554 Q_ASSERT_X(size, Q_FUNC_INFO, "invalid data size (0)");
555 Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
556
557 const char *src = static_cast<char *>(data);
558 char *dst = buffer.reserve(int(size));
559 std::copy(src, src + size, dst);
560
561 if (!isConnecting) {
562 // If we're still in connectToService, do not emit.
563 emit q_ptr->readyRead();
564 } // else connectToService must check and emit readyRead!
565}
566
567void QBluetoothSocketPrivate::writeComplete()
568{
569 _q_writeNotify();
570}
571
572QT_END_NAMESPACE
573

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