Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
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"
15#include "qbluetoothserver.h"
16#include "qbluetoothsocket.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
30
31namespace {
32
35
36QMap<quint16, QBluetoothServerPrivate *> &busyPSMs()
37{
38 static QMap<quint16, QBluetoothServerPrivate *> psms;
39 return psms;
40}
41
42QMap<quint16, QBluetoothServerPrivate *> &busyChannels()
43{
44 static QMap<quint16, QBluetoothServerPrivate *> channels;
45 return channels;
46}
47
49
50} // unnamed namespace
51
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
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
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");
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) {
199 if (it != busyChannels().end())
200 return it.value();
201 } else if (protocol == ServiceInfo::L2capProtocol) {
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) {
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) {
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
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
248{
250
251 if (d_ptr->listener) {
252 qCWarning(QT_BT_DARWIN) << "already in listen mode, close server first";
253 return false;
254 }
255
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;
263 return false;
264 }
265
266 const QBluetoothLocalDevice::HostMode hostMode = device.hostMode();
268 qCWarning(QT_BT_DARWIN) << "Bluetooth device is powered off";
269 d_ptr->m_lastError = 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";
294 }
295 } else {
296 if (d_ptr->psmIsBusy(port)) {
297 qCWarning(QT_BT_DARWIN) << "server port:" << port
298 << "already registered";
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";
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
335{
336 return d_ptr->pendingConnections.size();
337}
338
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
362{
364}
365
367{
368 return d_ptr->port;
369}
370
371void QBluetoothServer::setSecurityFlags(QBluetooth::SecurityFlags security)
372{
373 Q_UNUSED(security);
375}
376
377QBluetooth::SecurityFlags QBluetoothServer::securityFlags() const
378{
381}
382
IOBluetoothL2CAPChannel * channel
IOBluetoothDevice * device
\inmodule QtBluetooth
\inmodule QtBluetooth
HostMode
This enum describes the most of the local Bluetooth device.
QBluetoothAddress address() const
Returns the MAC address of this Bluetooth device.
QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol serverType, QBluetoothServer *parent)
QBluetoothServiceInfo::Protocol serverType
\inmodule QtBluetooth
bool hasPendingConnections() const
Returns true if a connection is pending, otherwise false.
void setSecurityFlags(QBluetooth::SecurityFlags security)
Sets the Bluetooth security flags to security.
QBluetoothServerPrivate * d_ptr
QBluetoothSocket * nextPendingConnection()
Returns a pointer to the QBluetoothSocket for the next pending connection.
bool listen(const QBluetoothAddress &address=QBluetoothAddress(), quint16 port=0)
Start listening for incoming connections to address on port.
void close()
Closes and resets the listening socket.
void errorOccurred(QBluetoothServer::Error error)
This signal is emitted when an error occurs.
quint16 serverPort() const
Returns the server port number.
QBluetoothAddress serverAddress() const
Returns the server address.
QBluetooth::SecurityFlags securityFlags() const
Returns the Bluetooth security flags.
void newConnection()
This signal is emitted when a new connection is available.
void setMaxPendingConnections(int numConnections)
Sets the maximum number of pending connections to numConnections.
\inmodule QtBluetooth
Protocol
This enum describes the socket protocol used by the service.
\inmodule QtBluetooth
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
#define this
Definition dialogs.cpp:9
QSet< QString >::iterator it
void qt_test_iobluetooth_runloop()
Definition btutility.mm:125
Combined button and popup list for selecting options.
QMap< quint16, QBluetoothServerPrivate * > & busyPSMs()
QMap< quint16, QBluetoothServerPrivate * > & busyChannels()
QMap< quint16, QBluetoothServerPrivate * >::iterator ServerMapIterator
#define Q_FUNC_INFO
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage return DBusPendingCall DBusPendingCall return DBusPendingCall return dbus_int32_t return DBusServer * server
EGLOutputPortEXT port
#define qCWarning(category,...)
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLuint GLuint end
GLenum type
GLuint GLuint64EXT address
GLuint64EXT * result
[6]
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
#define emit
#define Q_UNIMPLEMENTED()
#define Q_UNUSED(x)
unsigned short quint16
Definition qtypes.h:48
QMutex mutex
[2]
QReadWriteLock lock
[0]