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 "osx/osxbtsocketlistener_p.h"
41#include "qbluetoothserver_p.h"
42
43// The order is important: a workround for
44// a private header included by private header
45// (incorrectly handled dependencies).
46#include "qbluetoothsocketbase_p.h"
47#include "qbluetoothsocket_osx_p.h"
48
49#include "qbluetoothlocaldevice.h"
50#include "osx/osxbtutility_p.h"
51#include "osx/osxbluetooth_p.h"
52#include "qbluetoothserver.h"
53#include "qbluetoothsocket.h"
54
55#include <QtCore/qloggingcategory.h>
56#include <QtCore/qscopedpointer.h>
57#include <QtCore/qvariant.h>
58#include <QtCore/qglobal.h>
59#include <QtCore/qmutex.h>
60
61#include <Foundation/Foundation.h>
62
63#include <limits>
64
65QT_BEGIN_NAMESPACE
66
67namespace {
68
69using DarwinBluetooth::RetainPolicy;
70using ServiceInfo = QBluetoothServiceInfo;
71using ObjCListener = QT_MANGLE_NAMESPACE(OSXBTSocketListener);
72
73QMap<quint16, QBluetoothServerPrivate *> &busyPSMs()
74{
75 static QMap<quint16, QBluetoothServerPrivate *> psms;
76 return psms;
77}
78
79QMap<quint16, QBluetoothServerPrivate *> &busyChannels()
80{
81 static QMap<quint16, QBluetoothServerPrivate *> channels;
82 return channels;
83}
84
85typedef QMap<quint16, QBluetoothServerPrivate *>::iterator ServerMapIterator;
86
87}
88
89
90QBluetoothServerPrivate::QBluetoothServerPrivate(ServiceInfo::Protocol type)
91 : socket(nullptr),
92 maxPendingConnections(1),
93 securityFlags(QBluetooth::NoSecurity),
94 serverType(type),
95 q_ptr(nullptr),
96 m_lastError(QBluetoothServer::NoError),
97 port(0)
98{
99 if (serverType == ServiceInfo::UnknownProtocol)
100 qCWarning(QT_BT_OSX) << "unknown protocol";
101}
102
103QBluetoothServerPrivate::~QBluetoothServerPrivate()
104{
105 const QMutexLocker lock(&channelMapMutex());
106 unregisterServer(this);
107}
108
109bool QBluetoothServerPrivate::startListener(quint16 realPort)
110{
111 Q_ASSERT_X(realPort, Q_FUNC_INFO, "invalid port");
112
113 if (serverType == ServiceInfo::UnknownProtocol) {
114 qCWarning(QT_BT_OSX) << "invalid protocol";
115 return false;
116 }
117
118 if (!listener) {
119 listener.reset([[ObjCListener alloc] initWithListener:this],
120 RetainPolicy::noInitialRetain);
121 }
122
123 bool result = false;
124 if (serverType == ServiceInfo::RfcommProtocol)
125 result = [listener.getAs<ObjCListener>() listenRFCOMMConnectionsWithChannelID:realPort];
126 else
127 result = [listener.getAs<ObjCListener>() listenL2CAPConnectionsWithPSM:realPort];
128
129 if (!result)
130 listener.reset();
131
132 return result;
133}
134
135bool QBluetoothServerPrivate::isListening() const
136{
137 if (serverType == ServiceInfo::UnknownProtocol)
138 return false;
139
140 const QMutexLocker lock(&QBluetoothServerPrivate::channelMapMutex());
141 return QBluetoothServerPrivate::registeredServer(q_ptr->serverPort(), serverType);
142}
143
144void QBluetoothServerPrivate::stopListener()
145{
146 listener.reset();
147}
148
149void QBluetoothServerPrivate::openNotifyRFCOMM(void *generic)
150{
151 auto channel = static_cast<IOBluetoothRFCOMMChannel *>(generic);
152
153 Q_ASSERT_X(listener, Q_FUNC_INFO, "invalid listener (nil)");
154 Q_ASSERT_X(channel, Q_FUNC_INFO, "invalid channel (nil)");
155 Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
156
157 PendingConnection newConnection(channel, RetainPolicy::doInitialRetain);
158 pendingConnections.append(newConnection);
159
160 emit q_ptr->newConnection();
161}
162
163void QBluetoothServerPrivate::openNotifyL2CAP(void *generic)
164{
165 auto channel = static_cast<IOBluetoothL2CAPChannel *>(generic);
166
167 Q_ASSERT_X(listener, Q_FUNC_INFO, "invalid listener (nil)");
168 Q_ASSERT_X(channel, Q_FUNC_INFO, "invalid channel (nil)");
169 Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
170
171 PendingConnection newConnection(channel, RetainPolicy::doInitialRetain);
172 pendingConnections.append(newConnection);
173
174 emit q_ptr->newConnection();
175}
176
177QMutex &QBluetoothServerPrivate::channelMapMutex()
178{
179 static QMutex mutex;
180 return mutex;
181}
182
183bool QBluetoothServerPrivate::channelIsBusy(quint16 channelID)
184{
185 // External lock is required.
186 return busyChannels().contains(channelID);
187}
188
189quint16 QBluetoothServerPrivate::findFreeChannel()
190{
191 // External lock is required.
192 for (quint16 i = 1; i <= 30; ++i) {
193 if (!busyChannels().contains(i))
194 return i;
195 }
196
197 return 0; //Invalid port.
198}
199
200bool QBluetoothServerPrivate::psmIsBusy(quint16 psm)
201{
202 // External lock is required.
203 return busyPSMs().contains(psm);
204}
205
206quint16 QBluetoothServerPrivate::findFreePSM()
207{
208 // External lock is required.
209 for (quint16 i = 1, e = std::numeric_limits<qint16>::max(); i < e; i += 2) {
210 if (!psmIsBusy(i))
211 return i;
212 }
213
214 return 0; // Invalid PSM.
215}
216
217void QBluetoothServerPrivate::registerServer(QBluetoothServerPrivate *server, quint16 port)
218{
219 // External lock is required + port must be free.
220 Q_ASSERT_X(server, Q_FUNC_INFO, "invalid server (null)");
221
222 const ServiceInfo::Protocol type = server->serverType;
223 if (type == ServiceInfo::RfcommProtocol) {
224 Q_ASSERT_X(!channelIsBusy(port), Q_FUNC_INFO, "port is busy");
225 busyChannels()[port] = server;
226 } else if (type == ServiceInfo::L2capProtocol) {
227 Q_ASSERT_X(!psmIsBusy(port), Q_FUNC_INFO, "port is busy");
228 busyPSMs()[port] = server;
229 } else {
230 qCWarning(QT_BT_OSX) << "can not register a server "
231 "with unknown protocol type";
232 }
233}
234
235QBluetoothServerPrivate *QBluetoothServerPrivate::registeredServer(quint16 port, QBluetoothServiceInfo::Protocol protocol)
236{
237 // Eternal lock is required.
238 if (protocol == ServiceInfo::RfcommProtocol) {
239 ServerMapIterator it = busyChannels().find(port);
240 if (it != busyChannels().end())
241 return it.value();
242 } else if (protocol == ServiceInfo::L2capProtocol) {
243 ServerMapIterator it = busyPSMs().find(port);
244 if (it != busyPSMs().end())
245 return it.value();
246 } else {
247 qCWarning(QT_BT_OSX) << "invalid protocol";
248 }
249
250 return nullptr;
251}
252
253void QBluetoothServerPrivate::unregisterServer(QBluetoothServerPrivate *server)
254{
255 // External lock is required.
256 const ServiceInfo::Protocol type = server->serverType;
257 const quint16 port = server->port;
258
259 if (type == ServiceInfo::RfcommProtocol) {
260 ServerMapIterator it = busyChannels().find(port);
261 if (it != busyChannels().end()) {
262 busyChannels().erase(it);
263 } else {
264 qCWarning(QT_BT_OSX) << "server is not registered";
265 }
266 } else if (type == ServiceInfo::L2capProtocol) {
267 ServerMapIterator it = busyPSMs().find(port);
268 if (it != busyPSMs().end()) {
269 busyPSMs().erase(it);
270 } else {
271 qCWarning(QT_BT_OSX) << "server is not registered";
272 }
273 } else {
274 qCWarning(QT_BT_OSX) << "invalid protocol";
275 }
276}
277
278void QBluetoothServer::close()
279{
280 d_ptr->listener.reset();
281
282 // Needs a lock :(
283 const QMutexLocker lock(&d_ptr->channelMapMutex());
284 d_ptr->unregisterServer(d_ptr);
285 d_ptr->port = 0;
286}
287
288bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port)
289{
290 OSXBluetooth::qt_test_iobluetooth_runloop();
291
292 if (d_ptr->listener) {
293 qCWarning(QT_BT_OSX) << "already in listen mode, close server first";
294 return false;
295 }
296
297 const QBluetoothLocalDevice device(address);
298 if (!device.isValid()) {
299 qCWarning(QT_BT_OSX) << "device does not support Bluetooth or"
300 << address.toString()
301 << "is not a valid local adapter";
302 d_ptr->m_lastError = UnknownError;
303 emit error(UnknownError);
304 return false;
305 }
306
307 const QBluetoothLocalDevice::HostMode hostMode = device.hostMode();
308 if (hostMode == QBluetoothLocalDevice::HostPoweredOff) {
309 qCWarning(QT_BT_OSX) << "Bluetooth device is powered off";
310 d_ptr->m_lastError = PoweredOffError;
311 emit error(PoweredOffError);
312 return false;
313 }
314
315 const ServiceInfo::Protocol type = d_ptr->serverType;
316
317 if (type == ServiceInfo::UnknownProtocol) {
318 qCWarning(QT_BT_OSX) << "invalid protocol";
319 d_ptr->m_lastError = UnsupportedProtocolError;
320 emit error(d_ptr->m_lastError);
321 return false;
322 }
323
324 d_ptr->m_lastError = QBluetoothServer::NoError;
325
326 // Now we have to register a (fake) port, doing a proper (?) lock.
327 const QMutexLocker lock(&d_ptr->channelMapMutex());
328
329 if (port) {
330 if (type == ServiceInfo::RfcommProtocol) {
331 if (d_ptr->channelIsBusy(port)) {
332 qCWarning(QT_BT_OSX) << "server port:" << port
333 << "already registered";
334 d_ptr->m_lastError = ServiceAlreadyRegisteredError;
335 }
336 } else {
337 if (d_ptr->psmIsBusy(port)) {
338 qCWarning(QT_BT_OSX) << "server port:" << port
339 << "already registered";
340 d_ptr->m_lastError = ServiceAlreadyRegisteredError;
341 }
342 }
343 } else {
344 type == ServiceInfo::RfcommProtocol ? port = d_ptr->findFreeChannel()
345 : port = d_ptr->findFreePSM();
346 }
347
348 if (d_ptr->m_lastError != QBluetoothServer::NoError) {
349 emit error(d_ptr->m_lastError);
350 return false;
351 }
352
353 if (!port) {
354 qCWarning(QT_BT_OSX) << "all ports are busy";
355 d_ptr->m_lastError = ServiceAlreadyRegisteredError;
356 emit error(d_ptr->m_lastError);
357 return false;
358 }
359
360 // It's a fake port, the real one will be different
361 // (provided after a service was registered).
362 d_ptr->port = port;
363 d_ptr->registerServer(d_ptr, port);
364 d_ptr->listener.reset([[ObjCListener alloc] initWithListener:d_ptr],
365 RetainPolicy::noInitialRetain);
366
367 return true;
368}
369
370void QBluetoothServer::setMaxPendingConnections(int numConnections)
371{
372 d_ptr->maxPendingConnections = numConnections;
373}
374
375bool QBluetoothServer::hasPendingConnections() const
376{
377 return d_ptr->pendingConnections.size();
378}
379
380QBluetoothSocket *QBluetoothServer::nextPendingConnection()
381{
382 if (!d_ptr->pendingConnections.size())
383 return nullptr;
384
385 QScopedPointer<QBluetoothSocket> newSocket(new QBluetoothSocket);
386 QBluetoothServerPrivate::PendingConnection channel(d_ptr->pendingConnections.front());
387
388 // Remove it even if we have some errors below.
389 d_ptr->pendingConnections.pop_front();
390
391 if (d_ptr->serverType == ServiceInfo::RfcommProtocol) {
392 if (!static_cast<QBluetoothSocketPrivate *>(newSocket->d_ptr)->setRFCOMChannel(channel.getAs<IOBluetoothRFCOMMChannel>()))
393 return nullptr;
394 } else {
395 if (!static_cast<QBluetoothSocketPrivate *>(newSocket->d_ptr)->setL2CAPChannel(channel.getAs<IOBluetoothL2CAPChannel>()))
396 return nullptr;
397 }
398
399 return newSocket.take();
400}
401
402QBluetoothAddress QBluetoothServer::serverAddress() const
403{
404 return QBluetoothLocalDevice().address();
405}
406
407quint16 QBluetoothServer::serverPort() const
408{
409 return d_ptr->port;
410}
411
412void QBluetoothServer::setSecurityFlags(QBluetooth::SecurityFlags security)
413{
414 Q_UNUSED(security)
415 Q_UNIMPLEMENTED();
416}
417
418QBluetooth::SecurityFlags QBluetoothServer::securityFlags() const
419{
420 Q_UNIMPLEMENTED();
421 return QBluetooth::NoSecurity;
422}
423
424QT_END_NAMESPACE
425

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