/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "osx/osxbtsocketlistener_p.h" #include "qbluetoothserver_p.h" // The order is important: a workround for // a private header included by private header // (incorrectly handled dependencies). #include "qbluetoothsocketbase_p.h" #include "qbluetoothsocket_osx_p.h" #include "qbluetoothlocaldevice.h" #include "osx/osxbtutility_p.h" #include "osx/osxbluetooth_p.h" #include "qbluetoothserver.h" #include "qbluetoothsocket.h" #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE namespace { using DarwinBluetooth::RetainPolicy; using ServiceInfo = QBluetoothServiceInfo; using ObjCListener = QT_MANGLE_NAMESPACE(OSXBTSocketListener); QMap &busyPSMs() { static QMap psms; return psms; } QMap &busyChannels() { static QMap channels; return channels; } typedef QMap::iterator ServerMapIterator; } QBluetoothServerPrivate::QBluetoothServerPrivate(ServiceInfo::Protocol type, QBluetoothServer *parent) : socket(nullptr), maxPendingConnections(1), securityFlags(QBluetooth::NoSecurity), serverType(type), q_ptr(parent), m_lastError(QBluetoothServer::NoError), port(0) { if (serverType == ServiceInfo::UnknownProtocol) qCWarning(QT_BT_OSX) << "unknown protocol"; } QBluetoothServerPrivate::~QBluetoothServerPrivate() { const QMutexLocker lock(&channelMapMutex()); unregisterServer(this); } bool QBluetoothServerPrivate::startListener(quint16 realPort) { Q_ASSERT_X(realPort, Q_FUNC_INFO, "invalid port"); if (serverType == ServiceInfo::UnknownProtocol) { qCWarning(QT_BT_OSX) << "invalid protocol"; return false; } if (!listener) { listener.reset([[ObjCListener alloc] initWithListener:this], RetainPolicy::noInitialRetain); } bool result = false; if (serverType == ServiceInfo::RfcommProtocol) result = [listener.getAs() listenRFCOMMConnectionsWithChannelID:realPort]; else result = [listener.getAs() listenL2CAPConnectionsWithPSM:realPort]; if (!result) listener.reset(); return result; } bool QBluetoothServerPrivate::isListening() const { if (serverType == ServiceInfo::UnknownProtocol) return false; const QMutexLocker lock(&QBluetoothServerPrivate::channelMapMutex()); return QBluetoothServerPrivate::registeredServer(q_ptr->serverPort(), serverType); } void QBluetoothServerPrivate::stopListener() { listener.reset(); } void QBluetoothServerPrivate::openNotifyRFCOMM(void *generic) { auto channel = static_cast(generic); Q_ASSERT_X(listener, Q_FUNC_INFO, "invalid listener (nil)"); Q_ASSERT_X(channel, Q_FUNC_INFO, "invalid channel (nil)"); Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)"); PendingConnection newConnection(channel, RetainPolicy::doInitialRetain); pendingConnections.append(newConnection); emit q_ptr->newConnection(); } void QBluetoothServerPrivate::openNotifyL2CAP(void *generic) { auto channel = static_cast(generic); Q_ASSERT_X(listener, Q_FUNC_INFO, "invalid listener (nil)"); Q_ASSERT_X(channel, Q_FUNC_INFO, "invalid channel (nil)"); Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)"); PendingConnection newConnection(channel, RetainPolicy::doInitialRetain); pendingConnections.append(newConnection); emit q_ptr->newConnection(); } QMutex &QBluetoothServerPrivate::channelMapMutex() { static QMutex mutex; return mutex; } bool QBluetoothServerPrivate::channelIsBusy(quint16 channelID) { // External lock is required. return busyChannels().contains(channelID); } quint16 QBluetoothServerPrivate::findFreeChannel() { // External lock is required. for (quint16 i = 1; i <= 30; ++i) { if (!busyChannels().contains(i)) return i; } return 0; //Invalid port. } bool QBluetoothServerPrivate::psmIsBusy(quint16 psm) { // External lock is required. return busyPSMs().contains(psm); } quint16 QBluetoothServerPrivate::findFreePSM() { // External lock is required. for (quint16 i = 1, e = std::numeric_limits::max(); i < e; i += 2) { if (!psmIsBusy(i)) return i; } return 0; // Invalid PSM. } void QBluetoothServerPrivate::registerServer(QBluetoothServerPrivate *server, quint16 port) { // External lock is required + port must be free. Q_ASSERT_X(server, Q_FUNC_INFO, "invalid server (null)"); const ServiceInfo::Protocol type = server->serverType; if (type == ServiceInfo::RfcommProtocol) { Q_ASSERT_X(!channelIsBusy(port), Q_FUNC_INFO, "port is busy"); busyChannels()[port] = server; } else if (type == ServiceInfo::L2capProtocol) { Q_ASSERT_X(!psmIsBusy(port), Q_FUNC_INFO, "port is busy"); busyPSMs()[port] = server; } else { qCWarning(QT_BT_OSX) << "can not register a server " "with unknown protocol type"; } } QBluetoothServerPrivate *QBluetoothServerPrivate::registeredServer(quint16 port, QBluetoothServiceInfo::Protocol protocol) { // Eternal lock is required. if (protocol == ServiceInfo::RfcommProtocol) { ServerMapIterator it = busyChannels().find(port); if (it != busyChannels().end()) return it.value(); } else if (protocol == ServiceInfo::L2capProtocol) { ServerMapIterator it = busyPSMs().find(port); if (it != busyPSMs().end()) return it.value(); } else { qCWarning(QT_BT_OSX) << "invalid protocol"; } return nullptr; } void QBluetoothServerPrivate::unregisterServer(QBluetoothServerPrivate *server) { // External lock is required. const ServiceInfo::Protocol type = server->serverType; const quint16 port = server->port; if (type == ServiceInfo::RfcommProtocol) { ServerMapIterator it = busyChannels().find(port); if (it != busyChannels().end()) { busyChannels().erase(it); } else { qCWarning(QT_BT_OSX) << "server is not registered"; } } else if (type == ServiceInfo::L2capProtocol) { ServerMapIterator it = busyPSMs().find(port); if (it != busyPSMs().end()) { busyPSMs().erase(it); } else { qCWarning(QT_BT_OSX) << "server is not registered"; } } else { qCWarning(QT_BT_OSX) << "invalid protocol"; } } void QBluetoothServer::close() { d_ptr->listener.reset(); // Needs a lock :( const QMutexLocker lock(&d_ptr->channelMapMutex()); d_ptr->unregisterServer(d_ptr); d_ptr->port = 0; } bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port) { OSXBluetooth::qt_test_iobluetooth_runloop(); if (d_ptr->listener) { qCWarning(QT_BT_OSX) << "already in listen mode, close server first"; return false; } const QBluetoothLocalDevice device(address); if (!device.isValid()) { qCWarning(QT_BT_OSX) << "device does not support Bluetooth or" << address.toString() << "is not a valid local adapter"; d_ptr->m_lastError = UnknownError; emit error(UnknownError); return false; } const QBluetoothLocalDevice::HostMode hostMode = device.hostMode(); if (hostMode == QBluetoothLocalDevice::HostPoweredOff) { qCWarning(QT_BT_OSX) << "Bluetooth device is powered off"; d_ptr->m_lastError = PoweredOffError; emit error(PoweredOffError); return false; } const ServiceInfo::Protocol type = d_ptr->serverType; if (type == ServiceInfo::UnknownProtocol) { qCWarning(QT_BT_OSX) << "invalid protocol"; d_ptr->m_lastError = UnsupportedProtocolError; emit error(d_ptr->m_lastError); return false; } d_ptr->m_lastError = QBluetoothServer::NoError; // Now we have to register a (fake) port, doing a proper (?) lock. const QMutexLocker lock(&d_ptr->channelMapMutex()); if (port) { if (type == ServiceInfo::RfcommProtocol) { if (d_ptr->channelIsBusy(port)) { qCWarning(QT_BT_OSX) << "server port:" << port << "already registered"; d_ptr->m_lastError = ServiceAlreadyRegisteredError; } } else { if (d_ptr->psmIsBusy(port)) { qCWarning(QT_BT_OSX) << "server port:" << port << "already registered"; d_ptr->m_lastError = ServiceAlreadyRegisteredError; } } } else { type == ServiceInfo::RfcommProtocol ? port = d_ptr->findFreeChannel() : port = d_ptr->findFreePSM(); } if (d_ptr->m_lastError != QBluetoothServer::NoError) { emit error(d_ptr->m_lastError); return false; } if (!port) { qCWarning(QT_BT_OSX) << "all ports are busy"; d_ptr->m_lastError = ServiceAlreadyRegisteredError; emit error(d_ptr->m_lastError); return false; } // It's a fake port, the real one will be different // (provided after a service was registered). d_ptr->port = port; d_ptr->registerServer(d_ptr, port); d_ptr->listener.reset([[ObjCListener alloc] initWithListener:d_ptr], RetainPolicy::noInitialRetain); return true; } void QBluetoothServer::setMaxPendingConnections(int numConnections) { d_ptr->maxPendingConnections = numConnections; } bool QBluetoothServer::hasPendingConnections() const { return d_ptr->pendingConnections.size(); } QBluetoothSocket *QBluetoothServer::nextPendingConnection() { if (!d_ptr->pendingConnections.size()) return nullptr; QScopedPointer newSocket(new QBluetoothSocket); QBluetoothServerPrivate::PendingConnection channel(d_ptr->pendingConnections.front()); // Remove it even if we have some errors below. d_ptr->pendingConnections.pop_front(); if (d_ptr->serverType == ServiceInfo::RfcommProtocol) { if (!static_cast(newSocket->d_ptr)->setRFCOMChannel(channel.getAs())) return nullptr; } else { if (!static_cast(newSocket->d_ptr)->setL2CAPChannel(channel.getAs())) return nullptr; } return newSocket.take(); } QBluetoothAddress QBluetoothServer::serverAddress() const { return QBluetoothLocalDevice().address(); } quint16 QBluetoothServer::serverPort() const { return d_ptr->port; } void QBluetoothServer::setSecurityFlags(QBluetooth::SecurityFlags security) { Q_UNUSED(security) Q_UNIMPLEMENTED(); } QBluetooth::SecurityFlags QBluetoothServer::securityFlags() const { Q_UNIMPLEMENTED(); return QBluetooth::NoSecurity; } QT_END_NAMESPACE