Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qbluetoothsocket_bluezdbus.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 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 "qbluetoothsocket.h"
6
12#include "bluez/profile1_p.h"
15
16#include <QtBluetooth/qbluetoothdeviceinfo.h>
17#include <QtBluetooth/qbluetoothserviceinfo.h>
18
19#include <QtCore/qloggingcategory.h>
20#include <QtCore/qrandom.h>
21
22#include <QtNetwork/qlocalsocket.h>
23
24#include <unistd.h>
25
27
28using namespace Qt::StringLiterals;
29
31
33{
35
36 profileManager = new OrgBluezProfileManager1Interface(
37 QStringLiteral("org.bluez"),
38 QStringLiteral("/org/bluez"),
40 this);
41}
42
44{
45}
46
48{
49 switch (type) {
51 break;
55 return true;
56 }
57
58 return false;
59}
60
62 const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
63{
64 // TODO Remove when Bluez4 support dropped
65 // Only used by QBluetoothSocketPrivateBluez
69}
70
72{
74 QStringLiteral("/"),
76
77 bool ok = false;
78 const QString adapterPath = findAdapterForAddress(QBluetoothAddress(), &ok);
79 if (!ok)
80 return QString();
81
82 auto reply = manager.GetManagedObjects();
83 reply.waitForFinished();
84 if (reply.isError())
85 return QString();
86
87 ManagedObjectList objectList = reply.value();
89 it != objectList.constEnd(); ++it) {
90 const QDBusObjectPath &path = it.key();
91 const InterfaceList &ifaceList = it.value();
92
93 for (InterfaceList::const_iterator ifaceIter = ifaceList.constBegin();
94 ifaceIter != ifaceList.constEnd(); ++ifaceIter) {
95 if (ifaceIter.key() == QStringLiteral("org.bluez.Device1")) {
96 if (path.path().indexOf(adapterPath) != 0)
97 continue; // devices whose path does not start with same path we skip
98
101 if (device.adapter().path() != adapterPath)
102 continue;
103
104 const QBluetoothAddress btAddress(device.address());
105 if (btAddress.isNull() || btAddress != address)
106 continue;
107
108 return path.path();
109 }
110 }
111 }
112
113 return QString();
114}
115
117 const QBluetoothAddress &address, const QBluetoothUuid &uuid,
118 QIODevice::OpenMode openMode)
119{
120 Q_Q(QBluetoothSocket);
121
122 int i = 0;
123 bool success = false;
124 profileUuid = uuid.toString(QUuid::WithoutBraces);
125
126 if (profileContext) {
127 qCDebug(QT_BT_BLUEZ) << "Profile context still active. close socket first.";
129 return;
130 }
131
132
133 profileContext = new OrgBluezProfile1ContextInterface(this);
135 this, &QBluetoothSocketPrivateBluezDBus::remoteConnected);
136
137 for (i = 0; i < 10 && !success; i++) {
138 // profile registration might fail in case other service uses same path
139 // try 10 times and otherwise abort
140
141 profilePath = u"/qt/btsocket/%1%2/%3"_s.
144 arg(QRandomGenerator::global()->generate());
145
147 profilePath, profileContext, QDBusConnection::ExportAllSlots);
148 }
149
150 if (!success) {
151 // we could not register the profile
152 qCWarning(QT_BT_BLUEZ) << "Cannot export serial client profile on DBus";
153
154 delete profileContext;
155 profileContext = nullptr;
156
157 errorString = QBluetoothSocket::tr("Cannot export profile on DBus");
159
160 return;
161 }
162
163 QVariantMap profileOptions;
164 profileOptions.insert(QStringLiteral("Role"), QStringLiteral("client"));
165 profileOptions.insert(QStringLiteral("Service"), profileUuid);
166 profileOptions.insert(QStringLiteral("Name"),
167 QStringLiteral("QBluetoothSocket-%1").arg(QCoreApplication::applicationPid()));
168
169 // TODO support more profile parameter
170 // profileOptions.insert(QStringLiteral("Channel"), 0);
171
172 qCDebug(QT_BT_BLUEZ) << "Registering client profile on" << profilePath << "with options:";
173 qCDebug(QT_BT_BLUEZ) << profileOptions;
174 QDBusPendingReply<> reply = profileManager->RegisterProfile(
175 QDBusObjectPath(profilePath),
176 profileUuid,
177 profileOptions);
178 reply.waitForFinished();
179 if (reply.isError()) {
180 qCWarning(QT_BT_BLUEZ) << "Client profile registration failed:"
181 << reply.error().message();
182
184 errorString = QBluetoothSocket::tr("Cannot register profile on DBus");
186 return;
187 }
188
189 remoteDevicePath = findRemoteDevicePath(address);
190 if (remoteDevicePath.isEmpty()) {
191 qCWarning(QT_BT_BLUEZ) << "Unknown remote device:" << address
192 << "Try device discovery first";
193 clearSocket();
194
195 errorString = QBluetoothSocket::tr("Cannot find remote device");
197 return;
198 }
199
200 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
202 reply = device.ConnectProfile(profileUuid);
206
207 q->setOpenMode(openMode);
209}
210
213{
214 Q_Q(QBluetoothSocket);
215
217 if (reply.isError()) {
218 qCWarning(QT_BT_BLUEZ) << "Cannot connect to profile/service.";
219
220 clearSocket();
221
222 errorString = QBluetoothSocket::tr("Cannot connect to remote profile");
224 }
225 watcher->deleteLater();
226}
227
229 const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
230{
231 Q_Q(QBluetoothSocket);
232 QBluetoothUuid targetService;
233
234 targetService = service.serviceUuid();
235 if (targetService.isNull()) {
236 // Do we have serialport service class?
237 if (service.serviceClassUuids().contains(QBluetoothUuid::ServiceClassUuid::SerialPort))
239 }
240
241 if (targetService.isNull()) {
242 qCWarning(QT_BT_BLUEZ) << "Cannot find appropriate serviceUuid"
243 << "or SerialPort service class uuid";
244 errorString = QBluetoothSocket::tr("Missing serviceUuid or Serial Port service class uuid");
246 return;
247 }
248
249 if (service.socketProtocol() != QBluetoothServiceInfo::Protocol::UnknownProtocol)
250 socketType = service.socketProtocol();
251 qCDebug(QT_BT_BLUEZ) << "Socket protocol used:" << socketType;
252
253 connectToService(service.device().address(), targetService, openMode);
254}
255
257 const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode)
258{
259 Q_Q(QBluetoothSocket);
260
261 if (address.isNull()) {
262 qCWarning(QT_BT_BLUEZ) << "Invalid address to remote address passed.";
263 errorString = QBluetoothSocket::tr("Invalid Bluetooth address passed to connectToService()");
265 return;
266 }
267
268 if (uuid.isNull()) {
269 qCWarning(QT_BT_BLUEZ) << "Cannot find appropriate serviceUuid"
270 << "or SerialPort service class uuid";
271 errorString = QBluetoothSocket::tr("Missing serviceUuid or Serial Port service class uuid");
273 return;
274 }
275
277 qCWarning(QT_BT_BLUEZ) << "QBluetoothSocketPrivateBluezDBus::connectToService called on busy socket";
278 errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
280 return;
281 }
282
283 if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) {
284 qCWarning(QT_BT_BLUEZ) << "QBluetoothSocketPrivateBluezDBus::connectToService cannot "
285 "connect with 'UnknownProtocol' (type provided by given service)";
286 errorString = QBluetoothSocket::tr("Socket type not supported");
288 return;
289 }
290
291 if (!ensureNativeSocket(q->socketType())) {
292 errorString = QBluetoothSocket::tr("Socket type not supported");
294 return;
295 }
297}
298
300 const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
301{
302
303 Q_UNUSED(port);
306 Q_Q(QBluetoothSocket);
307
308 errorString = tr("Connecting to port is not supported via Bluez DBus");
310 qCWarning(QT_BT_BLUEZ) << "Connecting to port is not supported (Uuid required)";
311}
312
314{
315 if (localSocket) {
316 localSocket->close();
317 // delayed disconnected signal emission when localSocket closes
318 } else {
319 Q_Q(QBluetoothSocket);
320
321 clearSocket();
322 q->setOpenMode(QIODevice::NotOpen);
324 emit q->readChannelFinished();
325 }
326}
327
329{
330 bool ok = false;
331 const QString adapterPath = findAdapterForAddress(QBluetoothAddress(), &ok);
332 if (!ok)
333 return QString();
334
335 OrgBluezAdapter1Interface adapter(QStringLiteral("org.bluez"), adapterPath,
337 return QString(adapter.alias());
338}
339
341{
342 bool ok = false;
343 const QString adapterPath = findAdapterForAddress(QBluetoothAddress(), &ok);
344 if (!ok)
345 return QBluetoothAddress();
346
347 OrgBluezAdapter1Interface adapter(QStringLiteral("org.bluez"), adapterPath,
349 return QBluetoothAddress(adapter.address());
350}
351
353{
354 int descriptor = -1;
355
356 if (localSocket)
357 descriptor = int(localSocket->socketDescriptor());
358 if (descriptor == -1)
359 return 0;
360
363 socklen_t addrLength = sizeof(addr);
364
365 if (::getsockname(descriptor, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
366 return (addr.rc_channel);
369 socklen_t addrLength = sizeof(addr);
370
371 if (::getsockname(descriptor, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
372 return addr.l2_psm;
373 }
374
375 return 0;
376}
377
379{
380 if (remoteDevicePath.isEmpty())
381 return QString();
382
383 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
385 return device.alias();
386}
387
389{
390 if (remoteDevicePath.isEmpty())
391 return QBluetoothAddress();
392
393 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
395 return QBluetoothAddress(device.address());
396}
397
399{
400 int descriptor = -1;
401
402 if (localSocket)
403 descriptor = int(localSocket->socketDescriptor());
404 if (descriptor == -1)
405 return 0;
406
409 socklen_t addrLength = sizeof(addr);
410
411 if (::getpeername(descriptor, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
412 return addr.rc_channel;
415 socklen_t addrLength = sizeof(addr);
416
417 if (::getpeername(descriptor, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
418 return addr.l2_psm;
419 }
420
421 return 0;
422}
423
425{
426 Q_UNUSED(data);
427 Q_UNUSED(maxSize);
428
429 Q_Q(QBluetoothSocket);
430
432 errorString = QBluetoothSocket::tr("Cannot write while not connected");
434 return -1;
435 }
436
437 if (localSocket)
438 return localSocket->write(data, maxSize);
439
440 return -1;
441}
442
444{
445 Q_UNUSED(data);
446 Q_UNUSED(maxSize);
447
448 Q_Q(QBluetoothSocket);
449
451 errorString = QBluetoothSocket::tr("Cannot read while not connected");
453 return -1;
454 }
455
456 if (localSocket)
457 return localSocket->read(data, maxSize);
458
459 return -1;
460}
461
463{
464 abort();
465}
466
468 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
469{
470 Q_UNUSED(socketDescriptor);
472 Q_UNUSED(socketState);
474 return false;
475}
476
478{
479 if (localSocket)
480 return localSocket->bytesAvailable();
481
482 return 0;
483}
484
486{
487 if (localSocket)
488 return localSocket->canReadLine();
489
490 return false;
491}
492
494{
495 if (localSocket)
496 return localSocket->bytesToWrite();
497
498 return 0;
499}
500
501void QBluetoothSocketPrivateBluezDBus::remoteConnected(const QDBusUnixFileDescriptor &fd)
502{
503 Q_Q(QBluetoothSocket);
504
505 int descriptor = ::dup(fd.fileDescriptor());
506 localSocket = new QLocalSocket(this);
507 bool success = localSocket->setSocketDescriptor(
508 descriptor, QLocalSocket::ConnectedState, q->openMode());
509 if (!success || !localSocket->isValid()) {
511 delete localSocket;
512 localSocket = nullptr;
513 } else {
514 connect(localSocket, &QLocalSocket::readyRead,
517 this, &QBluetoothSocketPrivateBluezDBus::socketStateChanged);
520
521 socket = descriptor;
523 }
524}
525
526void QBluetoothSocketPrivateBluezDBus::socketStateChanged(QLocalSocket::LocalSocketState newState)
527{
528 Q_Q(QBluetoothSocket);
529
530 switch (newState) {
533 break;
535 clearSocket();
536 q->setOpenMode(QIODevice::NotOpen);
538 emit q->readChannelFinished();
539 break;
540 default:
541 // ConnectingState and ConnectedState not mapped
542 // (already set at the time when the socket is created)
543 break;
544 }
545}
546
547void QBluetoothSocketPrivateBluezDBus::clearSocket()
548{
549 Q_Q(QBluetoothSocket);
550
551 if (profilePath.isEmpty())
552 return;
553
554 qCDebug(QT_BT_BLUEZ) << "Clearing profile called for" << profilePath;
555
556 if (localSocket) {
557 localSocket->close();
558 localSocket->deleteLater();
559 localSocket = nullptr;
560 }
561
562 socket = -1;
563
565 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
567 auto reply = device.DisconnectProfile(profileUuid);
568 reply.waitForFinished();
569 if (reply.isError()) {
570 qCWarning(QT_BT_BLUEZ) << "Disconnect profile failed:"
571 << reply.error().message();
572 }
573 }
574
575 QDBusPendingReply<> reply = profileManager->UnregisterProfile(QDBusObjectPath(profilePath));
576 reply.waitForFinished();
577 if (reply.isError())
578 qCWarning(QT_BT_BLUEZ) << "Unregister profile:" << reply.error().message();
579
581
582 if (profileContext) {
583 delete profileContext;
584 profileContext = nullptr;
585 }
586
587 remoteDevicePath.clear();
588 profileUuid.clear();
589 profilePath.clear();
590}
592
593#include "moc_qbluetoothsocket_bluezdbus_p.cpp"
QString sanitizeNameForDBus(const QString &text)
QString findAdapterForAddress(const QBluetoothAddress &wantedAddress, bool *ok=nullptr)
IOBluetoothDevice * device
void newConnection(const QDBusUnixFileDescriptor &fd)
QDBusPendingReply UnregisterProfile(const QDBusObjectPath &profile)
QDBusPendingReply RegisterProfile(const QDBusObjectPath &profile, const QString &UUID, const QVariantMap &options)
\inmodule QtBluetooth
\inmodule QtBluetooth
Protocol
This enum describes the socket protocol used by the service.
QBluetoothSocket::OpenMode openMode
QBluetoothServiceInfo::Protocol socketType
QBluetoothSocket::SocketState state
qint64 readData(char *data, qint64 maxSize) override
void connectToService(const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode) override
void connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode) override
QBluetoothAddress peerAddress() const override
QBluetoothAddress localAddress() const override
qint64 writeData(const char *data, qint64 maxSize) override
bool setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType, QBluetoothSocket::SocketState socketState=QBluetoothSocket::SocketState::ConnectedState, QBluetoothSocket::OpenMode openMode=QBluetoothSocket::ReadWrite) override
void connectToServiceReplyHandler(QDBusPendingCallWatcher *)
bool ensureNativeSocket(QBluetoothServiceInfo::Protocol type) override
\inmodule QtBluetooth
SocketState
This enum describes the state of the Bluetooth socket.
\inmodule QtBluetooth
static qint64 applicationPid() Q_DECL_CONST_FUNCTION
QString applicationName
the name of this application
static QDBusConnection systemBus()
Returns a QDBusConnection object opened with the system bus.
void unregisterObject(const QString &path, UnregisterMode mode=UnregisterNode)
Unregisters an object that was registered with the registerObject() at the object path given by path ...
bool registerObject(const QString &path, QObject *object, RegisterOptions options=ExportAdaptors)
Registers the object object at path path and returns true if the registration was successful.
\inmodule QtDBus
void finished(QDBusPendingCallWatcher *self=nullptr)
This signal is emitted when the pending call has finished and its reply is available.
void readyRead()
This signal is emitted once every time new data is available for reading from the device's current re...
void bytesWritten(qint64 bytes)
This signal is emitted every time a payload of data has been written to the device's current write ch...
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
The QLocalSocket class provides a local socket.
bool setSocketDescriptor(qintptr socketDescriptor, LocalSocketState socketState=ConnectedState, OpenMode openMode=ReadWrite)
Initializes QLocalSocket with the native socket descriptor socketDescriptor.
virtual qint64 bytesToWrite() const override
\reimp
virtual bool canReadLine() const override
\reimp
LocalSocketState
This enum describes the different states in which a socket can be.
bool isValid() const
Returns true if the socket is valid and ready for use; otherwise returns false.
qintptr socketDescriptor() const
Returns the native socket descriptor of the QLocalSocket object if this is available; otherwise retur...
virtual void close() override
Closes the I/O device for the socket and calls disconnectFromServer() to close the socket's connectio...
virtual qint64 bytesAvailable() const override
\reimp
void stateChanged(QLocalSocket::LocalSocketState socketState)
This signal is emitted whenever QLocalSocket's state changes.
Definition qmap.h:186
iterator insert(const Key &key, const T &value)
Definition qmap.h:687
const_iterator constBegin() const
Definition qmap.h:599
const_iterator constEnd() const
Definition qmap.h:603
NetworkError error() const
Returns the error that was found during the processing of this request.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
void deleteLater()
\threadsafe
Definition qobject.cpp:2352
static Q_DECL_CONST_FUNCTION QRandomGenerator * global()
\threadsafe
Definition qrandom.h:275
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1107
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
@ WithoutBraces
Definition quuid.h:54
QString toString(StringFormat mode=WithBraces) const
Definition quuid.cpp:602
bool isNull() const noexcept
Returns true if this is the null UUID {00000000-0000-0000-0000-000000000000}; otherwise returns false...
Definition quuid.cpp:768
QSet< QString >::iterator it
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
Combined button and popup list for selecting options.
static QString findRemoteDevicePath(const QBluetoothAddress &address)
EGLOutputPortEXT port
static QT_BEGIN_NAMESPACE const char * socketType(QSocketNotifier::Type type)
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
GLenum type
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint64 GLenum GLint fd
GLenum const void * addr
GLuint GLuint64EXT address
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLsizei const GLchar *const * path
SSL_CTX int(*) void arg)
#define QStringLiteral(str)
#define tr(X)
#define emit
#define Q_UNUSED(x)
unsigned short quint16
Definition qtypes.h:43
long long qint64
Definition qtypes.h:55
QFutureWatcher< int > watcher
QNetworkAccessManager manager
QNetworkReply * reply