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
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
6
7#include "bluez/bluez_data_p.h"
8#include "bluez/bluez5_helper_p.h"
9#include "bluez/adapter1_bluez5_p.h"
10#include "bluez/device1_bluez5_p.h"
11#include "bluez/objectmanager_p.h"
12#include "bluez/profile1_p.h"
13#include "bluez/profile1context_p.h"
14#include "bluez/profilemanager1_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
26QT_BEGIN_NAMESPACE
27
28using namespace Qt::StringLiterals;
29using namespace QtBluetoothPrivate; // for D-Bus wrappers
30
31Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
32
34 : QBluetoothSocketBasePrivate{qq}
35{
36 secFlags = QBluetooth::Security::NoSecurity;
37}
38
42
43bool QBluetoothSocketPrivateBluezDBus::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
44{
45 switch (type) {
46 case QBluetoothServiceInfo::UnknownProtocol:
47 break;
48 case QBluetoothServiceInfo::RfcommProtocol:
49 case QBluetoothServiceInfo::L2capProtocol:
50 socketType = type;
51 return true;
52 }
53
54 return false;
55}
56
58 const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
59{
60 // TODO Remove when Bluez4 support dropped
61 // Only used by QBluetoothSocketPrivateBluez
62 Q_UNUSED(openMode);
63 Q_UNUSED(address);
64 Q_UNUSED(port);
65}
66
67static QString findRemoteDevicePath(const QBluetoothAddress &address)
68{
69 OrgFreedesktopDBusObjectManagerInterface manager(QStringLiteral("org.bluez"),
70 QStringLiteral("/"),
71 QDBusConnection::systemBus());
72
73 bool ok = false;
74 const QString adapterPath = findAdapterForAddress(QBluetoothAddress(), &ok);
75 if (!ok)
76 return QString();
77
78 auto reply = manager.GetManagedObjects();
79 reply.waitForFinished();
80 if (reply.isError())
81 return QString();
82
83 ManagedObjectList objectList = reply.value();
84 for (ManagedObjectList::const_iterator it = objectList.constBegin();
85 it != objectList.constEnd(); ++it) {
86 const QDBusObjectPath &path = it.key();
87 const InterfaceList &ifaceList = it.value();
88
89 for (InterfaceList::const_iterator ifaceIter = ifaceList.constBegin();
90 ifaceIter != ifaceList.constEnd(); ++ifaceIter) {
91 if (ifaceIter.key() == QStringLiteral("org.bluez.Device1")) {
92 if (path.path().indexOf(adapterPath) != 0)
93 continue; // devices whose path does not start with same path we skip
94
95 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"),
96 path.path(), QDBusConnection::systemBus());
97 if (device.adapter().path() != adapterPath)
98 continue;
99
100 const QBluetoothAddress btAddress(device.address());
101 if (btAddress.isNull() || btAddress != address)
102 continue;
103
104 return path.path();
105 }
106 }
107 }
108
109 return QString();
110}
111
113 const QBluetoothAddress &address, const QBluetoothUuid &uuid,
114 QIODevice::OpenMode openMode)
115{
116 Q_Q(QBluetoothSocket);
117
118 int i = 0;
119 bool success = false;
120 profileUuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
121 const QString remoteDeviceUuid = uuid.toString(QUuid::WithoutBraces);
122
123 if (!profileManager) {
124 profileManager = new OrgBluezProfileManager1Interface(
125 QStringLiteral("org.bluez"),
126 QStringLiteral("/org/bluez"),
127 QDBusConnection::systemBus(),
128 this);
129 }
130
131 if (profileContext) {
132 qCDebug(QT_BT_BLUEZ) << "Profile context still active. close socket first.";
133 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
134 return;
135 }
136
137
138 profileContext = new OrgBluezProfile1ContextInterface(this);
139 connect(profileContext, &OrgBluezProfile1ContextInterface::newConnection,
140 this, &QBluetoothSocketPrivateBluezDBus::remoteConnected);
141
142 for (i = 0; i < 10 && !success; i++) {
143 // profile registration might fail in case other service uses same path
144 // try 10 times and otherwise abort
145
146 profilePath = u"/qt/btsocket/%1%2/%3"_s.
147 arg(sanitizeNameForDBus(QCoreApplication::applicationName())).
148 arg(QCoreApplication::applicationPid()).
149 arg(QRandomGenerator::global()->generate());
150
151 success = QDBusConnection::systemBus().registerObject(
152 profilePath, profileContext, QDBusConnection::ExportAllSlots);
153 }
154
155 if (!success) {
156 // we could not register the profile
157 qCWarning(QT_BT_BLUEZ) << "Cannot export serial client profile on DBus";
158
159 delete profileContext;
160 profileContext = nullptr;
161
162 errorString = QBluetoothSocket::tr("Cannot export profile on DBus");
163 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
164
165 return;
166 }
167
168 QVariantMap profileOptions;
169 profileOptions.insert(QStringLiteral("Role"), QStringLiteral("client"));
170 profileOptions.insert(QStringLiteral("Service"), remoteDeviceUuid);
171 profileOptions.insert(QStringLiteral("Name"),
172 QStringLiteral("QBluetoothSocket-%1").arg(QCoreApplication::applicationPid()));
173
174 // TODO support more profile parameter
175 // profileOptions.insert(QStringLiteral("Channel"), 0);
176
177 qCDebug(QT_BT_BLUEZ) << "Registering client profile on" << profilePath << "with options:";
178 qCDebug(QT_BT_BLUEZ) << profileOptions;
179 QDBusPendingReply<> reply = profileManager->RegisterProfile(
180 QDBusObjectPath(profilePath),
181 profileUuid,
182 profileOptions);
183 reply.waitForFinished();
184 if (reply.isError()) {
185 qCWarning(QT_BT_BLUEZ) << "Client profile registration failed:"
186 << reply.error().message();
187
188 QDBusConnection::systemBus().unregisterObject(profilePath);
189 errorString = QBluetoothSocket::tr("Cannot register profile on DBus");
190 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
191 return;
192 }
193
194 remoteDevicePath = findRemoteDevicePath(address);
195 if (remoteDevicePath.isEmpty()) {
196 qCWarning(QT_BT_BLUEZ) << "Unknown remote device:" << address
197 << "Try device discovery first";
198 clearSocket();
199
200 errorString = QBluetoothSocket::tr("Cannot find remote device");
201 q->setSocketError(QBluetoothSocket::SocketError::HostNotFoundError);
202 return;
203 }
204
205 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
206 QDBusConnection::systemBus());
207 reply = device.ConnectProfile(remoteDeviceUuid);
208 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
209 connect(watcher, &QDBusPendingCallWatcher::finished,
210 this, &QBluetoothSocketPrivateBluezDBus::connectToServiceReplyHandler);
211
212 q->setOpenMode(openMode);
213 q->setSocketState(QBluetoothSocket::SocketState::ConnectingState);
214}
215
216void QBluetoothSocketPrivateBluezDBus::connectToServiceReplyHandler(
217 QDBusPendingCallWatcher *watcher)
218{
219 Q_Q(QBluetoothSocket);
220
221 QDBusPendingReply<> reply = *watcher;
222 if (reply.isError()) {
223 qCWarning(QT_BT_BLUEZ) << "Cannot connect to profile/service.";
224
225 clearSocket();
226
227 errorString = QBluetoothSocket::tr("Cannot connect to remote profile");
228 q->setSocketError(QBluetoothSocket::SocketError::HostNotFoundError);
229 }
230 watcher->deleteLater();
231
232 // QTBUG-82413, unregisterProfile at profileUuid,
233 // so it can be registered for new devices connecting to the same profile UUID.
234 if (profileManager) {
235 qCDebug(QT_BT_BLUEZ) << "Unregistering client profile on" << profilePath
236 << "in connectToServiceReplyHandler() callback.";
237
238 QDBusPendingReply<> reply = profileManager->UnregisterProfile(QDBusObjectPath(profilePath));
239 reply.waitForFinished();
240 if (reply.isError())
241 qCWarning(QT_BT_BLUEZ) << "Unregister profile:" << reply.error().message();
242
243 QDBusConnection::systemBus().unregisterObject(profilePath);
244
245 delete profileManager;
246 profileManager = nullptr;
247 }
248}
249
251 const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
252{
253 Q_Q(QBluetoothSocket);
254 QBluetoothUuid targetService;
255
256 targetService = service.serviceUuid();
257 if (targetService.isNull()) {
258 // Do we have serialport service class?
259 if (service.serviceClassUuids().contains(QBluetoothUuid::ServiceClassUuid::SerialPort))
260 targetService = QBluetoothUuid::ServiceClassUuid::SerialPort;
261 }
262
263 if (targetService.isNull()) {
264 qCWarning(QT_BT_BLUEZ) << "Cannot find appropriate serviceUuid"
265 << "or SerialPort service class uuid";
266 errorString = QBluetoothSocket::tr("Missing serviceUuid or Serial Port service class uuid");
267 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
268 return;
269 }
270
271 if (service.socketProtocol() != QBluetoothServiceInfo::Protocol::UnknownProtocol)
272 socketType = service.socketProtocol();
273 qCDebug(QT_BT_BLUEZ) << "Socket protocol used:" << socketType;
274
275 connectToService(service.device().address(), targetService, openMode);
276}
277
279 const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode)
280{
281 Q_Q(QBluetoothSocket);
282
283 if (address.isNull()) {
284 qCWarning(QT_BT_BLUEZ) << "Invalid address to remote address passed.";
285 errorString = QBluetoothSocket::tr("Invalid Bluetooth address passed to connectToService()");
286 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
287 return;
288 }
289
290 if (uuid.isNull()) {
291 qCWarning(QT_BT_BLUEZ) << "Cannot find appropriate serviceUuid"
292 << "or SerialPort service class uuid";
293 errorString = QBluetoothSocket::tr("Missing serviceUuid or Serial Port service class uuid");
294 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
295 return;
296 }
297
298 if (q->state() != QBluetoothSocket::SocketState::UnconnectedState) {
299 qCWarning(QT_BT_BLUEZ) << "QBluetoothSocketPrivateBluezDBus::connectToService called on busy socket";
300 errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
301 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
302 return;
303 }
304
305 if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) {
306 qCWarning(QT_BT_BLUEZ) << "QBluetoothSocketPrivateBluezDBus::connectToService cannot "
307 "connect with 'UnknownProtocol' (type provided by given service)";
308 errorString = QBluetoothSocket::tr("Socket type not supported");
309 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
310 return;
311 }
312
313 if (!ensureNativeSocket(q->socketType())) {
314 errorString = QBluetoothSocket::tr("Socket type not supported");
315 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
316 return;
317 }
318 connectToServiceHelper(address, uuid, openMode);
319}
320
322 const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
323{
324
325 Q_UNUSED(port);
326 Q_UNUSED(address);
327 Q_UNUSED(openMode);
328 Q_Q(QBluetoothSocket);
329
330 errorString = tr("Connecting to port is not supported via Bluez DBus");
331 q->setSocketError(QBluetoothSocket::SocketError::ServiceNotFoundError);
332 qCWarning(QT_BT_BLUEZ) << "Connecting to port is not supported (Uuid required)";
333}
334
336{
337 if (localSocket) {
338 localSocket->close();
339 // delayed disconnected signal emission when localSocket closes
340 } else {
341 Q_Q(QBluetoothSocket);
342
343 clearSocket();
344 q->setOpenMode(QIODevice::NotOpen);
345 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
346 emit q->readChannelFinished();
347 }
348}
349
351{
352 bool ok = false;
353 const QString adapterPath = findAdapterForAddress(QBluetoothAddress(), &ok);
354 if (!ok)
355 return QString();
356
357 OrgBluezAdapter1Interface adapter(QStringLiteral("org.bluez"), adapterPath,
358 QDBusConnection::systemBus());
359 return QString(adapter.alias());
360}
361
363{
364 bool ok = false;
365 const QString adapterPath = findAdapterForAddress(QBluetoothAddress(), &ok);
366 if (!ok)
367 return QBluetoothAddress();
368
369 OrgBluezAdapter1Interface adapter(QStringLiteral("org.bluez"), adapterPath,
370 QDBusConnection::systemBus());
371 return QBluetoothAddress(adapter.address());
372}
373
375{
376 int descriptor = -1;
377
378 if (localSocket)
379 descriptor = int(localSocket->socketDescriptor());
380 if (descriptor == -1)
381 return 0;
382
383 if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
384 sockaddr_rc addr;
385 socklen_t addrLength = sizeof(addr);
386
387 if (::getsockname(descriptor, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
388 return (addr.rc_channel);
389 } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
390 sockaddr_l2 addr;
391 socklen_t addrLength = sizeof(addr);
392
393 if (::getsockname(descriptor, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
394 return addr.l2_psm;
395 }
396
397 return 0;
398}
399
401{
402 if (remoteDevicePath.isEmpty())
403 return QString();
404
405 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
406 QDBusConnection::systemBus());
407 return device.alias();
408}
409
411{
412 if (remoteDevicePath.isEmpty())
413 return QBluetoothAddress();
414
415 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
416 QDBusConnection::systemBus());
417 return QBluetoothAddress(device.address());
418}
419
421{
422 int descriptor = -1;
423
424 if (localSocket)
425 descriptor = int(localSocket->socketDescriptor());
426 if (descriptor == -1)
427 return 0;
428
429 if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
430 sockaddr_rc addr;
431 socklen_t addrLength = sizeof(addr);
432
433 if (::getpeername(descriptor, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
434 return addr.rc_channel;
435 } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
436 sockaddr_l2 addr;
437 socklen_t addrLength = sizeof(addr);
438
439 if (::getpeername(descriptor, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
440 return addr.l2_psm;
441 }
442
443 return 0;
444}
445
446qint64 QBluetoothSocketPrivateBluezDBus::writeData(const char *data, qint64 maxSize)
447{
448 Q_UNUSED(data);
449 Q_UNUSED(maxSize);
450
451 Q_Q(QBluetoothSocket);
452
453 if (state != QBluetoothSocket::SocketState::ConnectedState) {
454 errorString = QBluetoothSocket::tr("Cannot write while not connected");
455 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
456 return -1;
457 }
458
459 if (localSocket)
460 return localSocket->write(data, maxSize);
461
462 return -1;
463}
464
466{
467 Q_UNUSED(data);
468 Q_UNUSED(maxSize);
469
470 Q_Q(QBluetoothSocket);
471
472 if (state != QBluetoothSocket::SocketState::ConnectedState) {
473 errorString = QBluetoothSocket::tr("Cannot read while not connected");
474 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
475 return -1;
476 }
477
478 if (localSocket)
479 return localSocket->read(data, maxSize);
480
481 return -1;
482}
483
488
489bool QBluetoothSocketPrivateBluezDBus::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
490 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
491{
492 Q_UNUSED(socketDescriptor);
493 Q_UNUSED(socketType);
494 Q_UNUSED(socketState);
495 Q_UNUSED(openMode);
496 return false;
497}
498
500{
501 if (localSocket)
502 return localSocket->bytesAvailable();
503
504 return 0;
505}
506
508{
509 if (localSocket)
510 return localSocket->canReadLine();
511
512 return false;
513}
514
516{
517 if (localSocket)
518 return localSocket->bytesToWrite();
519
520 return 0;
521}
522
523void QBluetoothSocketPrivateBluezDBus::remoteConnected(const QDBusUnixFileDescriptor &fd)
524{
525 Q_Q(QBluetoothSocket);
526
527 int descriptor = ::dup(fd.fileDescriptor());
528 localSocket = new QLocalSocket(this);
529 bool success = localSocket->setSocketDescriptor(
530 descriptor, QLocalSocket::ConnectedState, q->openMode());
531 if (!success || !localSocket->isValid()) {
532 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
533 delete localSocket;
534 localSocket = nullptr;
535 } else {
536 connect(localSocket, &QLocalSocket::readyRead,
537 q, &QBluetoothSocket::readyRead);
538 connect(localSocket, &QLocalSocket::stateChanged,
539 this, &QBluetoothSocketPrivateBluezDBus::socketStateChanged);
540 connect(localSocket, &QLocalSocket::bytesWritten,
541 q, &QBluetoothSocket::bytesWritten);
542
543 socket = descriptor;
544 q->setSocketState(QBluetoothSocket::SocketState::ConnectedState);
545 }
546}
547
548void QBluetoothSocketPrivateBluezDBus::socketStateChanged(QLocalSocket::LocalSocketState newState)
549{
550 Q_Q(QBluetoothSocket);
551
552 switch (newState) {
553 case QLocalSocket::ClosingState:
554 q->setSocketState(QBluetoothSocket::SocketState::ClosingState);
555 break;
556 case QLocalSocket::UnconnectedState:
557 clearSocket();
558 q->setOpenMode(QIODevice::NotOpen);
559 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
560 emit q->readChannelFinished();
561 break;
562 default:
563 // ConnectingState and ConnectedState not mapped
564 // (already set at the time when the socket is created)
565 break;
566 }
567}
568
569void QBluetoothSocketPrivateBluezDBus::clearSocket()
570{
571 Q_Q(QBluetoothSocket);
572
573 if (profilePath.isEmpty())
574 return;
575
576 qCDebug(QT_BT_BLUEZ) << "Clearing profile called for" << profilePath;
577
578 if (localSocket) {
579 localSocket->close();
580 localSocket->deleteLater();
581 localSocket = nullptr;
582 }
583
584 socket = -1;
585
586 if (q->state() == QBluetoothSocket::SocketState::ConnectedState) {
587 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
588 QDBusConnection::systemBus());
589 auto reply = device.DisconnectProfile(profileUuid);
590 reply.waitForFinished();
591 if (reply.isError()) {
592 qCWarning(QT_BT_BLUEZ) << "Disconnect profile failed:"
593 << reply.error().message();
594 }
595 }
596
597 if (profileContext) {
598 delete profileContext;
599 profileContext = nullptr;
600 }
601
602 if (profileManager) {
603 qCDebug(QT_BT_BLUEZ) << "Unregistering client profile on" << profilePath
604 << "in clearSocket().";
605
606 QDBusPendingReply<> reply = profileManager->UnregisterProfile(QDBusObjectPath(profilePath));
607 reply.waitForFinished();
608 if (reply.isError())
609 qCWarning(QT_BT_BLUEZ) << "Unregister profile:" << reply.error().message();
610
611 QDBusConnection::systemBus().unregisterObject(profilePath);
612
613 delete profileManager;
614 profileManager = nullptr;
615 }
616
617 remoteDevicePath.clear();
618 profileUuid.clear();
619 profilePath.clear();
620}
621QT_END_NAMESPACE
622
623#include "moc_qbluetoothsocket_bluezdbus_p.cpp"
QMap< QDBusObjectPath, InterfaceList > ManagedObjectList
qint64 readData(char *data, qint64 maxSize) override
void connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode) override
void connectToService(const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode) override
void connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode) override
void connectToServiceHelper(const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode)
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 connectToService(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode) override
\inmodule QtBluetooth
static QString findRemoteDevicePath(const QBluetoothAddress &address)
unsigned short l2_psm