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