Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qbluetoothserver_macos.mm
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "darwin/btsocketlistener_p.h"
6
7// The order is important: a workround for
8// a private header included by private header
9// (incorrectly handled dependencies).
12
14#include "darwin/btutility_p.h"
17
18#include <QtCore/qloggingcategory.h>
19#include <QtCore/qvariant.h>
20#include <QtCore/qglobal.h>
21#include <QtCore/qmutex.h>
22
23#include <Foundation/Foundation.h>
24
25#include <IOBluetooth/IOBluetooth.h>
26
27#include <limits>
28
29QT_BEGIN_NAMESPACE
30
31namespace {
32
33using DarwinBluetooth::RetainPolicy;
34using ServiceInfo = QBluetoothServiceInfo;
35
37{
38 static QMap<quint16, QBluetoothServerPrivate *> psms;
39 return psms;
40}
41
43{
44 static QMap<quint16, QBluetoothServerPrivate *> channels;
45 return channels;
46}
47
49
50} // unnamed namespace
51
52QBluetoothServerPrivate::QBluetoothServerPrivate(ServiceInfo::Protocol type,
53 QBluetoothServer *parent)
54 : serverType(type),
55 q_ptr(parent),
56 port(0)
57{
58 if (serverType == ServiceInfo::UnknownProtocol)
59 qCWarning(QT_BT_DARWIN) << "unknown protocol";
60}
61
63{
64 const QMutexLocker lock(&channelMapMutex());
65 unregisterServer(this);
66}
67
68bool QBluetoothServerPrivate::startListener(quint16 realPort)
69{
70 Q_ASSERT_X(realPort, Q_FUNC_INFO, "invalid port");
71
72 if (serverType == ServiceInfo::UnknownProtocol) {
73 qCWarning(QT_BT_DARWIN) << "invalid protocol";
74 return false;
75 }
76
77 if (!listener) {
78 listener.reset([[DarwinBTSocketListener alloc] initWithListener:this],
79 RetainPolicy::noInitialRetain);
80 }
81
82 bool result = false;
83 if (serverType == ServiceInfo::RfcommProtocol)
84 result = [listener.getAs<DarwinBTSocketListener>() listenRFCOMMConnectionsWithChannelID:realPort];
85 else
86 result = [listener.getAs<DarwinBTSocketListener>() listenL2CAPConnectionsWithPSM:realPort];
87
88 if (!result)
89 listener.reset();
90
91 return result;
92}
93
94bool QBluetoothServerPrivate::isListening() const
95{
96 if (serverType == ServiceInfo::UnknownProtocol)
97 return false;
98
99 const QMutexLocker lock(&QBluetoothServerPrivate::channelMapMutex());
100 return QBluetoothServerPrivate::registeredServer(q_ptr->serverPort(), serverType);
101}
102
103void QBluetoothServerPrivate::stopListener()
104{
105 listener.reset();
106}
107
108void QBluetoothServerPrivate::openNotifyRFCOMM(void *generic)
109{
110 auto channel = static_cast<IOBluetoothRFCOMMChannel *>(generic);
111
112 Q_ASSERT_X(listener, Q_FUNC_INFO, "invalid listener (nil)");
113 Q_ASSERT_X(channel, Q_FUNC_INFO, "invalid channel (nil)");
114 Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
115
116 PendingConnection newConnection(channel, RetainPolicy::doInitialRetain);
117 pendingConnections.append(newConnection);
118
119 emit q_ptr->newConnection();
120}
121
122void QBluetoothServerPrivate::openNotifyL2CAP(void *generic)
123{
124 auto channel = static_cast<IOBluetoothL2CAPChannel *>(generic);
125
126 Q_ASSERT_X(listener, Q_FUNC_INFO, "invalid listener (nil)");
127 Q_ASSERT_X(channel, Q_FUNC_INFO, "invalid channel (nil)");
128 Q_ASSERT_X(q_ptr, Q_FUNC_INFO, "invalid q_ptr (null)");
129
130 PendingConnection newConnection(channel, RetainPolicy::doInitialRetain);
131 pendingConnections.append(newConnection);
132
133 emit q_ptr->newConnection();
134}
135
136QMutex &QBluetoothServerPrivate::channelMapMutex()
137{
138 static QMutex mutex;
139 return mutex;
140}
141
142bool QBluetoothServerPrivate::channelIsBusy(quint16 channelID)
143{
144 // External lock is required.
145 return busyChannels().contains(channelID);
146}
147
148quint16 QBluetoothServerPrivate::findFreeChannel()
149{
150 // External lock is required.
151 for (quint16 i = 1; i <= 30; ++i) {
152 if (!busyChannels().contains(i))
153 return i;
154 }
155
156 return 0; //Invalid port.
157}
158
159bool QBluetoothServerPrivate::psmIsBusy(quint16 psm)
160{
161 // External lock is required.
162 return busyPSMs().contains(psm);
163}
164
165quint16 QBluetoothServerPrivate::findFreePSM()
166{
167 // External lock is required.
168 for (quint16 i = 1, e = std::numeric_limits<qint16>::max(); i < e; i += 2) {
169 if (!psmIsBusy(i))
170 return i;
171 }
172
173 return 0; // Invalid PSM.
174}
175
176void QBluetoothServerPrivate::registerServer(QBluetoothServerPrivate *server, quint16 port)
177{
178 // External lock is required + port must be free.
179 Q_ASSERT_X(server, Q_FUNC_INFO, "invalid server (null)");
180
181 const ServiceInfo::Protocol type = server->serverType;
182 if (type == ServiceInfo::RfcommProtocol) {
183 Q_ASSERT_X(!channelIsBusy(port), Q_FUNC_INFO, "port is busy");
184 busyChannels()[port] = server;
185 } else if (type == ServiceInfo::L2capProtocol) {
186 Q_ASSERT_X(!psmIsBusy(port), Q_FUNC_INFO, "port is busy");
187 busyPSMs()[port] = server;
188 } else {
189 qCWarning(QT_BT_DARWIN) << "can not register a server "
190 "with unknown protocol type";
191 }
192}
193
194QBluetoothServerPrivate *QBluetoothServerPrivate::registeredServer(quint16 port, QBluetoothServiceInfo::Protocol protocol)
195{
196 // Eternal lock is required.
197 if (protocol == ServiceInfo::RfcommProtocol) {
198 ServerMapIterator it = busyChannels().find(port);
199 if (it != busyChannels().end())
200 return it.value();
201 } else if (protocol == ServiceInfo::L2capProtocol) {
202 ServerMapIterator it = busyPSMs().find(port);
203 if (it != busyPSMs().end())
204 return it.value();
205 } else {
206 qCWarning(QT_BT_DARWIN) << "invalid protocol";
207 }
208
209 return nullptr;
210}
211
212void QBluetoothServerPrivate::unregisterServer(QBluetoothServerPrivate *server)
213{
214 // External lock is required.
215 const ServiceInfo::Protocol type = server->serverType;
216 const quint16 port = server->port;
217
218 if (type == ServiceInfo::RfcommProtocol) {
219 ServerMapIterator it = busyChannels().find(port);
220 if (it != busyChannels().end()) {
221 busyChannels().erase(it);
222 } else {
223 qCWarning(QT_BT_DARWIN) << "server is not registered";
224 }
225 } else if (type == ServiceInfo::L2capProtocol) {
226 ServerMapIterator it = busyPSMs().find(port);
227 if (it != busyPSMs().end()) {
228 busyPSMs().erase(it);
229 } else {
230 qCWarning(QT_BT_DARWIN) << "server is not registered";
231 }
232 } else {
233 qCWarning(QT_BT_DARWIN) << "invalid protocol";
234 }
235}
236
237void QBluetoothServer::close()
238{
239 d_ptr->listener.reset();
240
241 // Needs a lock :(
242 const QMutexLocker lock(&d_ptr->channelMapMutex());
243 d_ptr->unregisterServer(d_ptr);
244 d_ptr->port = 0;
245}
246
247bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port)
248{
249 DarwinBluetooth::qt_test_iobluetooth_runloop();
250
251 if (d_ptr->listener) {
252 qCWarning(QT_BT_DARWIN) << "already in listen mode, close server first";
253 return false;
254 }
255
256 const QBluetoothLocalDevice device(address);
257 if (!device.isValid()) {
258 qCWarning(QT_BT_DARWIN) << "device does not support Bluetooth or"
259 << address.toString()
260 << "is not a valid local adapter";
261 d_ptr->m_lastError = UnknownError;
262 emit errorOccurred(UnknownError);
263 return false;
264 }
265
266 const QBluetoothLocalDevice::HostMode hostMode = device.hostMode();
267 if (hostMode == QBluetoothLocalDevice::HostPoweredOff) {
268 qCWarning(QT_BT_DARWIN) << "Bluetooth device is powered off";
269 d_ptr->m_lastError = PoweredOffError;
270 emit errorOccurred(PoweredOffError);
271 return false;
272 }
273
274 const ServiceInfo::Protocol type = d_ptr->serverType;
275
276 if (type == ServiceInfo::UnknownProtocol) {
277 qCWarning(QT_BT_DARWIN) << "invalid protocol";
278 d_ptr->m_lastError = UnsupportedProtocolError;
279 emit errorOccurred(d_ptr->m_lastError);
280 return false;
281 }
282
283 d_ptr->m_lastError = QBluetoothServer::NoError;
284
285 // Now we have to register a (fake) port, doing a proper (?) lock.
286 const QMutexLocker lock(&d_ptr->channelMapMutex());
287
288 if (port) {
289 if (type == ServiceInfo::RfcommProtocol) {
290 if (d_ptr->channelIsBusy(port)) {
291 qCWarning(QT_BT_DARWIN) << "server port:" << port
292 << "already registered";
293 d_ptr->m_lastError = ServiceAlreadyRegisteredError;
294 }
295 } else {
296 if (d_ptr->psmIsBusy(port)) {
297 qCWarning(QT_BT_DARWIN) << "server port:" << port
298 << "already registered";
299 d_ptr->m_lastError = ServiceAlreadyRegisteredError;
300 }
301 }
302 } else {
303 type == ServiceInfo::RfcommProtocol ? port = d_ptr->findFreeChannel()
304 : port = d_ptr->findFreePSM();
305 }
306
307 if (d_ptr->m_lastError != QBluetoothServer::NoError) {
308 emit errorOccurred(d_ptr->m_lastError);
309 return false;
310 }
311
312 if (!port) {
313 qCWarning(QT_BT_DARWIN) << "all ports are busy";
314 d_ptr->m_lastError = ServiceAlreadyRegisteredError;
315 emit errorOccurred(d_ptr->m_lastError);
316 return false;
317 }
318
319 // It's a fake port, the real one will be different
320 // (provided after a service was registered).
321 d_ptr->port = port;
322 d_ptr->registerServer(d_ptr, port);
323 d_ptr->listener.reset([[DarwinBTSocketListener alloc] initWithListener:d_ptr],
324 RetainPolicy::noInitialRetain);
325
326 return true;
327}
328
329void QBluetoothServer::setMaxPendingConnections(int numConnections)
330{
331 d_ptr->maxPendingConnections = numConnections;
332}
333
334bool QBluetoothServer::hasPendingConnections() const
335{
336 return d_ptr->pendingConnections.size();
337}
338
339QBluetoothSocket *QBluetoothServer::nextPendingConnection()
340{
341 if (!d_ptr->pendingConnections.size())
342 return nullptr;
343
344 std::unique_ptr<QBluetoothSocket> newSocket = std::make_unique<QBluetoothSocket>();
345 QBluetoothServerPrivate::PendingConnection channel(d_ptr->pendingConnections.front());
346
347 // Remove it even if we have some errors below.
348 d_ptr->pendingConnections.pop_front();
349
350 if (d_ptr->serverType == ServiceInfo::RfcommProtocol) {
351 if (!static_cast<QBluetoothSocketPrivateDarwin *>(newSocket->d_ptr)->setRFCOMChannel(channel.getAs<IOBluetoothRFCOMMChannel>()))
352 return nullptr;
353 } else {
354 if (!static_cast<QBluetoothSocketPrivateDarwin *>(newSocket->d_ptr)->setL2CAPChannel(channel.getAs<IOBluetoothL2CAPChannel>()))
355 return nullptr;
356 }
357
358 return newSocket.release();
359}
360
361QBluetoothAddress QBluetoothServer::serverAddress() const
362{
363 return QBluetoothLocalDevice().address();
364}
365
366quint16 QBluetoothServer::serverPort() const
367{
368 return d_ptr->port;
369}
370
371void QBluetoothServer::setSecurityFlags(QBluetooth::SecurityFlags security)
372{
373 Q_UNUSED(security);
374 Q_UNIMPLEMENTED();
375}
376
377QBluetooth::SecurityFlags QBluetoothServer::securityFlags() const
378{
379 Q_UNIMPLEMENTED();
380 return QBluetooth::Security::NoSecurity;
381}
382
383QT_END_NAMESPACE
QMap< quint16, QBluetoothServerPrivate * > & busyPSMs()
QMap< quint16, QBluetoothServerPrivate * > & busyChannels()
QMap< quint16, QBluetoothServerPrivate * >::iterator ServerMapIterator