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