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

source code of qtconnectivity/src/bluetooth/qbluetoothsocket_bluezdbus.cpp