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