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
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
36
40
54
56 const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
57{
58 // TODO Remove when Bluez4 support dropped
59 // Only used by QBluetoothSocketPrivateBluez
63}
64
66{
68 QStringLiteral("/"),
70
71 bool ok = false;
72 const QString adapterPath = findAdapterForAddress(QBluetoothAddress(), &ok);
73 if (!ok)
74 return QString();
75
76 auto reply = manager.GetManagedObjects();
77 reply.waitForFinished();
78 if (reply.isError())
79 return QString();
80
81 ManagedObjectList objectList = reply.value();
82 for (ManagedObjectList::const_iterator it = objectList.constBegin();
83 it != objectList.constEnd(); ++it) {
84 const QDBusObjectPath &path = it.key();
85 const InterfaceList &ifaceList = it.value();
86
87 for (InterfaceList::const_iterator ifaceIter = ifaceList.constBegin();
88 ifaceIter != ifaceList.constEnd(); ++ifaceIter) {
89 if (ifaceIter.key() == QStringLiteral("org.bluez.Device1")) {
90 if (path.path().indexOf(adapterPath) != 0)
91 continue; // devices whose path does not start with same path we skip
92
95 if (device.adapter().path() != adapterPath)
96 continue;
97
98 const QBluetoothAddress btAddress(device.address());
99 if (btAddress.isNull() || btAddress != address)
100 continue;
101
102 return path.path();
103 }
104 }
105 }
106
107 return QString();
108}
109
111 const QBluetoothAddress &address, const QBluetoothUuid &uuid,
112 QIODevice::OpenMode openMode)
113{
114 Q_Q(QBluetoothSocket);
115
116 int i = 0;
117 bool success = false;
118 profileUuid = uuid.toString(QUuid::WithoutBraces);
119
120 if (!profileManager) {
121 profileManager = new OrgBluezProfileManager1Interface(
122 QStringLiteral("org.bluez"),
123 QStringLiteral("/org/bluez"),
125 this);
126 }
127
128 if (profileContext) {
129 qCDebug(QT_BT_BLUEZ) << "Profile context still active. close socket first.";
131 return;
132 }
133
134
135 profileContext = new OrgBluezProfile1ContextInterface(this);
137 this, &QBluetoothSocketPrivateBluezDBus::remoteConnected);
138
139 for (i = 0; i < 10 && !success; i++) {
140 // profile registration might fail in case other service uses same path
141 // try 10 times and otherwise abort
142
143 profilePath = u"/qt/btsocket/%1%2/%3"_s.
146 arg(QRandomGenerator::global()->generate());
147
148 success = QDBusConnection::systemBus().registerObject(
149 profilePath, profileContext, QDBusConnection::ExportAllSlots);
150 }
151
152 if (!success) {
153 // we could not register the profile
154 qCWarning(QT_BT_BLUEZ) << "Cannot export serial client profile on DBus";
155
156 delete profileContext;
157 profileContext = nullptr;
158
159 errorString = QBluetoothSocket::tr("Cannot export profile on DBus");
161
162 return;
163 }
164
165 QVariantMap profileOptions;
166 profileOptions.insert(QStringLiteral("Role"), QStringLiteral("client"));
167 profileOptions.insert(QStringLiteral("Service"), profileUuid);
168 profileOptions.insert(QStringLiteral("Name"),
169 QStringLiteral("QBluetoothSocket-%1").arg(QCoreApplication::applicationPid()));
170
171 // TODO support more profile parameter
172 // profileOptions.insert(QStringLiteral("Channel"), 0);
173
174 qCDebug(QT_BT_BLUEZ) << "Registering client profile on" << profilePath << "with options:";
175 qCDebug(QT_BT_BLUEZ) << profileOptions;
176 QDBusPendingReply<> reply = profileManager->RegisterProfile(
177 QDBusObjectPath(profilePath),
178 profileUuid,
179 profileOptions);
180 reply.waitForFinished();
181 if (reply.isError()) {
182 qCWarning(QT_BT_BLUEZ) << "Client profile registration failed:"
183 << reply.error().message();
184
185 QDBusConnection::systemBus().unregisterObject(profilePath);
186 errorString = QBluetoothSocket::tr("Cannot register profile on DBus");
188 return;
189 }
190
191 remoteDevicePath = findRemoteDevicePath(address);
192 if (remoteDevicePath.isEmpty()) {
193 qCWarning(QT_BT_BLUEZ) << "Unknown remote device:" << address
194 << "Try device discovery first";
195 clearSocket();
196
197 errorString = QBluetoothSocket::tr("Cannot find remote device");
199 return;
200 }
201
202 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
204 reply = device.ConnectProfile(profileUuid);
208
209 q->setOpenMode(openMode);
211}
212
215{
216 Q_Q(QBluetoothSocket);
217
219 if (reply.isError()) {
220 qCWarning(QT_BT_BLUEZ) << "Cannot connect to profile/service.";
221
222 clearSocket();
223
224 errorString = QBluetoothSocket::tr("Cannot connect to remote profile");
226 }
227 watcher->deleteLater();
228
229 // QTBUG-82413, unregisterProfile at profileUuid,
230 // so it can be registered for new devices connecting to the same profile UUID.
231 if (profileManager) {
232 qCDebug(QT_BT_BLUEZ) << "Unregistering client profile on" << profilePath
233 << "in connectToServiceReplyHandler() callback.";
234
235 QDBusPendingReply<> reply = profileManager->UnregisterProfile(QDBusObjectPath(profilePath));
236 reply.waitForFinished();
237 if (reply.isError())
238 qCWarning(QT_BT_BLUEZ) << "Unregister profile:" << reply.error().message();
239
240 QDBusConnection::systemBus().unregisterObject(profilePath);
241
242 delete profileManager;
243 profileManager = nullptr;
244 }
245}
246
248 const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
249{
250 Q_Q(QBluetoothSocket);
251 QBluetoothUuid targetService;
252
253 targetService = service.serviceUuid();
254 if (targetService.isNull()) {
255 // Do we have serialport service class?
256 if (service.serviceClassUuids().contains(QBluetoothUuid::ServiceClassUuid::SerialPort))
258 }
259
260 if (targetService.isNull()) {
261 qCWarning(QT_BT_BLUEZ) << "Cannot find appropriate serviceUuid"
262 << "or SerialPort service class uuid";
263 errorString = QBluetoothSocket::tr("Missing serviceUuid or Serial Port service class uuid");
265 return;
266 }
267
268 if (service.socketProtocol() != QBluetoothServiceInfo::Protocol::UnknownProtocol)
269 socketType = service.socketProtocol();
270 qCDebug(QT_BT_BLUEZ) << "Socket protocol used:" << socketType;
271
272 connectToService(service.device().address(), targetService, openMode);
273}
274
276 const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode)
277{
278 Q_Q(QBluetoothSocket);
279
280 if (address.isNull()) {
281 qCWarning(QT_BT_BLUEZ) << "Invalid address to remote address passed.";
282 errorString = QBluetoothSocket::tr("Invalid Bluetooth address passed to connectToService()");
284 return;
285 }
286
287 if (uuid.isNull()) {
288 qCWarning(QT_BT_BLUEZ) << "Cannot find appropriate serviceUuid"
289 << "or SerialPort service class uuid";
290 errorString = QBluetoothSocket::tr("Missing serviceUuid or Serial Port service class uuid");
292 return;
293 }
294
296 qCWarning(QT_BT_BLUEZ) << "QBluetoothSocketPrivateBluezDBus::connectToService called on busy socket";
297 errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
299 return;
300 }
301
302 if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) {
303 qCWarning(QT_BT_BLUEZ) << "QBluetoothSocketPrivateBluezDBus::connectToService cannot "
304 "connect with 'UnknownProtocol' (type provided by given service)";
305 errorString = QBluetoothSocket::tr("Socket type not supported");
307 return;
308 }
309
310 if (!ensureNativeSocket(q->socketType())) {
311 errorString = QBluetoothSocket::tr("Socket type not supported");
313 return;
314 }
316}
317
319 const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
320{
321
322 Q_UNUSED(port);
325 Q_Q(QBluetoothSocket);
326
327 errorString = tr("Connecting to port is not supported via Bluez DBus");
329 qCWarning(QT_BT_BLUEZ) << "Connecting to port is not supported (Uuid required)";
330}
331
333{
334 if (localSocket) {
335 localSocket->close();
336 // delayed disconnected signal emission when localSocket closes
337 } else {
338 Q_Q(QBluetoothSocket);
339
340 clearSocket();
341 q->setOpenMode(QIODevice::NotOpen);
343 emit q->readChannelFinished();
344 }
345}
346
348{
349 bool ok = false;
350 const QString adapterPath = findAdapterForAddress(QBluetoothAddress(), &ok);
351 if (!ok)
352 return QString();
353
354 OrgBluezAdapter1Interface adapter(QStringLiteral("org.bluez"), adapterPath,
356 return QString(adapter.alias());
357}
358
360{
361 bool ok = false;
362 const QString adapterPath = findAdapterForAddress(QBluetoothAddress(), &ok);
363 if (!ok)
364 return QBluetoothAddress();
365
366 OrgBluezAdapter1Interface adapter(QStringLiteral("org.bluez"), adapterPath,
368 return QBluetoothAddress(adapter.address());
369}
370
372{
373 int descriptor = -1;
374
375 if (localSocket)
376 descriptor = int(localSocket->socketDescriptor());
377 if (descriptor == -1)
378 return 0;
379
382 socklen_t addrLength = sizeof(addr);
383
384 if (::getsockname(descriptor, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
385 return (addr.rc_channel);
388 socklen_t addrLength = sizeof(addr);
389
390 if (::getsockname(descriptor, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
391 return addr.l2_psm;
392 }
393
394 return 0;
395}
396
398{
399 if (remoteDevicePath.isEmpty())
400 return QString();
401
402 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
404 return device.alias();
405}
406
408{
409 if (remoteDevicePath.isEmpty())
410 return QBluetoothAddress();
411
412 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
414 return QBluetoothAddress(device.address());
415}
416
418{
419 int descriptor = -1;
420
421 if (localSocket)
422 descriptor = int(localSocket->socketDescriptor());
423 if (descriptor == -1)
424 return 0;
425
428 socklen_t addrLength = sizeof(addr);
429
430 if (::getpeername(descriptor, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
431 return addr.rc_channel;
434 socklen_t addrLength = sizeof(addr);
435
436 if (::getpeername(descriptor, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
437 return addr.l2_psm;
438 }
439
440 return 0;
441}
442
444{
445 Q_UNUSED(data);
446 Q_UNUSED(maxSize);
447
448 Q_Q(QBluetoothSocket);
449
451 errorString = QBluetoothSocket::tr("Cannot write while not connected");
453 return -1;
454 }
455
456 if (localSocket)
457 return localSocket->write(data, maxSize);
458
459 return -1;
460}
461
463{
464 Q_UNUSED(data);
465 Q_UNUSED(maxSize);
466
467 Q_Q(QBluetoothSocket);
468
470 errorString = QBluetoothSocket::tr("Cannot read while not connected");
472 return -1;
473 }
474
475 if (localSocket)
476 return localSocket->read(data, maxSize);
477
478 return -1;
479}
480
485
487 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
488{
489 Q_UNUSED(socketDescriptor);
491 Q_UNUSED(socketState);
493 return false;
494}
495
497{
498 if (localSocket)
499 return localSocket->bytesAvailable();
500
501 return 0;
502}
503
505{
506 if (localSocket)
507 return localSocket->canReadLine();
508
509 return false;
510}
511
513{
514 if (localSocket)
515 return localSocket->bytesToWrite();
516
517 return 0;
518}
519
520void QBluetoothSocketPrivateBluezDBus::remoteConnected(const QDBusUnixFileDescriptor &fd)
521{
522 Q_Q(QBluetoothSocket);
523
524 int descriptor = ::dup(fd.fileDescriptor());
525 localSocket = new QLocalSocket(this);
526 bool success = localSocket->setSocketDescriptor(
527 descriptor, QLocalSocket::ConnectedState, q->openMode());
528 if (!success || !localSocket->isValid()) {
530 delete localSocket;
531 localSocket = nullptr;
532 } else {
533 connect(localSocket, &QLocalSocket::readyRead,
536 this, &QBluetoothSocketPrivateBluezDBus::socketStateChanged);
539
540 socket = descriptor;
542 }
543}
544
545void QBluetoothSocketPrivateBluezDBus::socketStateChanged(QLocalSocket::LocalSocketState newState)
546{
547 Q_Q(QBluetoothSocket);
548
549 switch (newState) {
552 break;
554 clearSocket();
555 q->setOpenMode(QIODevice::NotOpen);
557 emit q->readChannelFinished();
558 break;
559 default:
560 // ConnectingState and ConnectedState not mapped
561 // (already set at the time when the socket is created)
562 break;
563 }
564}
565
566void QBluetoothSocketPrivateBluezDBus::clearSocket()
567{
568 Q_Q(QBluetoothSocket);
569
570 if (profilePath.isEmpty())
571 return;
572
573 qCDebug(QT_BT_BLUEZ) << "Clearing profile called for" << profilePath;
574
575 if (localSocket) {
576 localSocket->close();
577 localSocket->deleteLater();
578 localSocket = nullptr;
579 }
580
581 socket = -1;
582
584 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
586 auto reply = device.DisconnectProfile(profileUuid);
587 reply.waitForFinished();
588 if (reply.isError()) {
589 qCWarning(QT_BT_BLUEZ) << "Disconnect profile failed:"
590 << reply.error().message();
591 }
592 }
593
594 if (profileContext) {
595 delete profileContext;
596 profileContext = nullptr;
597 }
598
599 if (profileManager) {
600 qCDebug(QT_BT_BLUEZ) << "Unregistering client profile on" << profilePath
601 << "in clearSocket().";
602
603 QDBusPendingReply<> reply = profileManager->UnregisterProfile(QDBusObjectPath(profilePath));
604 reply.waitForFinished();
605 if (reply.isError())
606 qCWarning(QT_BT_BLUEZ) << "Unregister profile:" << reply.error().message();
607
608 QDBusConnection::systemBus().unregisterObject(profilePath);
609
610 delete profileManager;
611 profileManager = nullptr;
612 }
613
614 remoteDevicePath.clear();
615 profileUuid.clear();
616 profilePath.clear();
617}
619
620#include "moc_qbluetoothsocket_bluezdbus_p.cpp"
QString sanitizeNameForDBus(const QString &text)
QString findAdapterForAddress(const QBluetoothAddress &wantedAddress, bool *ok=nullptr)
QMap< QDBusObjectPath, InterfaceList > ManagedObjectList
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.
\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:187
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
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:2960
void deleteLater()
\threadsafe
Definition qobject.cpp:2435
static Q_DECL_CONST_FUNCTION QRandomGenerator * global()
\threadsafe
Definition qrandom.h:275
const_iterator constBegin() const noexcept
Definition qset.h:139
const_iterator constEnd() const noexcept
Definition qset.h:143
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
@ WithoutBraces
Definition quuid.h:54
QString toString(StringFormat mode=WithBraces) const
Definition quuid.cpp:650
bool isNull() const noexcept
Returns true if this is the null UUID {00000000-0000-0000-0000-000000000000}; otherwise returns false...
Definition quuid.cpp:818
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)
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum type
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:48
long long qint64
Definition qtypes.h:60
QFutureWatcher< int > watcher
QNetworkAccessManager manager
QNetworkReply * reply
unsigned short l2_psm
quint8 rc_channel