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;
29
30Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
31
32QBluetoothSocketPrivateBluezDBus::QBluetoothSocketPrivateBluezDBus()
33{
34 secFlags = QBluetooth::Security::NoSecurity;
35
36 profileManager = new OrgBluezProfileManager1Interface(
37 QStringLiteral("org.bluez"),
38 QStringLiteral("/org/bluez"),
39 QDBusConnection::systemBus(),
40 this);
41}
42
43QBluetoothSocketPrivateBluezDBus::~QBluetoothSocketPrivateBluezDBus()
44{
45}
46
47bool QBluetoothSocketPrivateBluezDBus::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
48{
49 switch (type) {
50 case QBluetoothServiceInfo::UnknownProtocol:
51 break;
52 case QBluetoothServiceInfo::RfcommProtocol:
53 case QBluetoothServiceInfo::L2capProtocol:
54 socketType = type;
55 return true;
56 }
57
58 return false;
59}
60
61void QBluetoothSocketPrivateBluezDBus::connectToServiceHelper(
62 const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
63{
64 // TODO Remove when Bluez4 support dropped
65 // Only used by QBluetoothSocketPrivateBluez
66 Q_UNUSED(openMode);
67 Q_UNUSED(address);
68 Q_UNUSED(port);
69}
70
71static QString findRemoteDevicePath(const QBluetoothAddress &address)
72{
73 OrgFreedesktopDBusObjectManagerInterface manager(QStringLiteral("org.bluez"),
74 QStringLiteral("/"),
75 QDBusConnection::systemBus());
76
77 bool ok = false;
78 const QString adapterPath = findAdapterForAddress(wantedAddress: QBluetoothAddress(), ok: &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();
88 for (ManagedObjectList::const_iterator it = objectList.constBegin();
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(s: adapterPath) != 0)
97 continue; // devices whose path does not start with same path we skip
98
99 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"),
100 path.path(), QDBusConnection::systemBus());
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
116void QBluetoothSocketPrivateBluezDBus::connectToServiceHelper(
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(mode: QUuid::WithoutBraces);
125
126 if (profileContext) {
127 qCDebug(QT_BT_BLUEZ) << "Profile context still active. close socket first.";
128 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
129 return;
130 }
131
132
133 profileContext = new OrgBluezProfile1ContextInterface(this);
134 connect(sender: profileContext, signal: &OrgBluezProfile1ContextInterface::newConnection,
135 context: this, slot: &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.
142 arg(a: sanitizeNameForDBus(text: QCoreApplication::applicationName())).
143 arg(a: QCoreApplication::applicationPid()).
144 arg(a: QRandomGenerator::global()->generate());
145
146 success = QDBusConnection::systemBus().registerObject(
147 path: profilePath, object: profileContext, options: 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(s: "Cannot export profile on DBus");
158 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
159
160 return;
161 }
162
163 QVariantMap profileOptions;
164 profileOptions.insert(QStringLiteral("Role"), QStringLiteral("client"));
165 profileOptions.insert(QStringLiteral("Service"), value: profileUuid);
166 profileOptions.insert(QStringLiteral("Name"),
167 QStringLiteral("QBluetoothSocket-%1").arg(a: 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 profile: QDBusObjectPath(profilePath),
176 UUID: profileUuid,
177 options: profileOptions);
178 reply.waitForFinished();
179 if (reply.isError()) {
180 qCWarning(QT_BT_BLUEZ) << "Client profile registration failed:"
181 << reply.error().message();
182
183 QDBusConnection::systemBus().unregisterObject(path: profilePath);
184 errorString = QBluetoothSocket::tr(s: "Cannot register profile on DBus");
185 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
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(s: "Cannot find remote device");
196 q->setSocketError(QBluetoothSocket::SocketError::HostNotFoundError);
197 return;
198 }
199
200 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
201 QDBusConnection::systemBus());
202 reply = device.ConnectProfile(UUID: profileUuid);
203 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
204 connect(sender: watcher, signal: &QDBusPendingCallWatcher::finished,
205 context: this, slot: &QBluetoothSocketPrivateBluezDBus::connectToServiceReplyHandler);
206
207 q->setOpenMode(openMode);
208 q->setSocketState(QBluetoothSocket::SocketState::ConnectingState);
209}
210
211void QBluetoothSocketPrivateBluezDBus::connectToServiceReplyHandler(
212 QDBusPendingCallWatcher *watcher)
213{
214 Q_Q(QBluetoothSocket);
215
216 QDBusPendingReply<> reply = *watcher;
217 if (reply.isError()) {
218 qCWarning(QT_BT_BLUEZ) << "Cannot connect to profile/service.";
219
220 clearSocket();
221
222 errorString = QBluetoothSocket::tr(s: "Cannot connect to remote profile");
223 q->setSocketError(QBluetoothSocket::SocketError::HostNotFoundError);
224 }
225 watcher->deleteLater();
226}
227
228void QBluetoothSocketPrivateBluezDBus::connectToService(
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(t: QBluetoothUuid::ServiceClassUuid::SerialPort))
238 targetService = 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(s: "Missing serviceUuid or Serial Port service class uuid");
245 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
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(address: service.device().address(), uuid: targetService, openMode);
254}
255
256void QBluetoothSocketPrivateBluezDBus::connectToService(
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(s: "Invalid Bluetooth address passed to connectToService()");
264 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
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(s: "Missing serviceUuid or Serial Port service class uuid");
272 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
273 return;
274 }
275
276 if (q->state() != QBluetoothSocket::SocketState::UnconnectedState) {
277 qCWarning(QT_BT_BLUEZ) << "QBluetoothSocketPrivateBluezDBus::connectToService called on busy socket";
278 errorString = QBluetoothSocket::tr(s: "Trying to connect while connection is in progress");
279 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
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(s: "Socket type not supported");
287 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
288 return;
289 }
290
291 if (!ensureNativeSocket(type: q->socketType())) {
292 errorString = QBluetoothSocket::tr(s: "Socket type not supported");
293 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
294 return;
295 }
296 connectToServiceHelper(address, uuid, openMode);
297}
298
299void QBluetoothSocketPrivateBluezDBus::connectToService(
300 const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
301{
302
303 Q_UNUSED(port);
304 Q_UNUSED(address);
305 Q_UNUSED(openMode);
306 Q_Q(QBluetoothSocket);
307
308 errorString = tr(s: "Connecting to port is not supported via Bluez DBus");
309 q->setSocketError(QBluetoothSocket::SocketError::ServiceNotFoundError);
310 qCWarning(QT_BT_BLUEZ) << "Connecting to port is not supported (Uuid required)";
311}
312
313void QBluetoothSocketPrivateBluezDBus::abort()
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);
323 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
324 emit q->readChannelFinished();
325 }
326}
327
328QString QBluetoothSocketPrivateBluezDBus::localName() const
329{
330 bool ok = false;
331 const QString adapterPath = findAdapterForAddress(wantedAddress: QBluetoothAddress(), ok: &ok);
332 if (!ok)
333 return QString();
334
335 OrgBluezAdapter1Interface adapter(QStringLiteral("org.bluez"), adapterPath,
336 QDBusConnection::systemBus());
337 return QString(adapter.alias());
338}
339
340QBluetoothAddress QBluetoothSocketPrivateBluezDBus::localAddress() const
341{
342 bool ok = false;
343 const QString adapterPath = findAdapterForAddress(wantedAddress: QBluetoothAddress(), ok: &ok);
344 if (!ok)
345 return QBluetoothAddress();
346
347 OrgBluezAdapter1Interface adapter(QStringLiteral("org.bluez"), adapterPath,
348 QDBusConnection::systemBus());
349 return QBluetoothAddress(adapter.address());
350}
351
352quint16 QBluetoothSocketPrivateBluezDBus::localPort() const
353{
354 int descriptor = -1;
355
356 if (localSocket)
357 descriptor = int(localSocket->socketDescriptor());
358 if (descriptor == -1)
359 return 0;
360
361 if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
362 sockaddr_rc addr;
363 socklen_t addrLength = sizeof(addr);
364
365 if (::getsockname(fd: descriptor, addr: reinterpret_cast<sockaddr *>(&addr), len: &addrLength) == 0)
366 return (addr.rc_channel);
367 } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
368 sockaddr_l2 addr;
369 socklen_t addrLength = sizeof(addr);
370
371 if (::getsockname(fd: descriptor, addr: reinterpret_cast<sockaddr *>(&addr), len: &addrLength) == 0)
372 return addr.l2_psm;
373 }
374
375 return 0;
376}
377
378QString QBluetoothSocketPrivateBluezDBus::peerName() const
379{
380 if (remoteDevicePath.isEmpty())
381 return QString();
382
383 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
384 QDBusConnection::systemBus());
385 return device.alias();
386}
387
388QBluetoothAddress QBluetoothSocketPrivateBluezDBus::peerAddress() const
389{
390 if (remoteDevicePath.isEmpty())
391 return QBluetoothAddress();
392
393 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
394 QDBusConnection::systemBus());
395 return QBluetoothAddress(device.address());
396}
397
398quint16 QBluetoothSocketPrivateBluezDBus::peerPort() const
399{
400 int descriptor = -1;
401
402 if (localSocket)
403 descriptor = int(localSocket->socketDescriptor());
404 if (descriptor == -1)
405 return 0;
406
407 if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
408 sockaddr_rc addr;
409 socklen_t addrLength = sizeof(addr);
410
411 if (::getpeername(fd: descriptor, addr: reinterpret_cast<sockaddr *>(&addr), len: &addrLength) == 0)
412 return addr.rc_channel;
413 } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
414 sockaddr_l2 addr;
415 socklen_t addrLength = sizeof(addr);
416
417 if (::getpeername(fd: descriptor, addr: reinterpret_cast<sockaddr *>(&addr), len: &addrLength) == 0)
418 return addr.l2_psm;
419 }
420
421 return 0;
422}
423
424qint64 QBluetoothSocketPrivateBluezDBus::writeData(const char *data, qint64 maxSize)
425{
426 Q_UNUSED(data);
427 Q_UNUSED(maxSize);
428
429 Q_Q(QBluetoothSocket);
430
431 if (state != QBluetoothSocket::SocketState::ConnectedState) {
432 errorString = QBluetoothSocket::tr(s: "Cannot write while not connected");
433 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
434 return -1;
435 }
436
437 if (localSocket)
438 return localSocket->write(data, len: maxSize);
439
440 return -1;
441}
442
443qint64 QBluetoothSocketPrivateBluezDBus::readData(char *data, qint64 maxSize)
444{
445 Q_UNUSED(data);
446 Q_UNUSED(maxSize);
447
448 Q_Q(QBluetoothSocket);
449
450 if (state != QBluetoothSocket::SocketState::ConnectedState) {
451 errorString = QBluetoothSocket::tr(s: "Cannot read while not connected");
452 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
453 return -1;
454 }
455
456 if (localSocket)
457 return localSocket->read(data, maxlen: maxSize);
458
459 return -1;
460}
461
462void QBluetoothSocketPrivateBluezDBus::close()
463{
464 abort();
465}
466
467bool QBluetoothSocketPrivateBluezDBus::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
468 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
469{
470 Q_UNUSED(socketDescriptor);
471 Q_UNUSED(socketType);
472 Q_UNUSED(socketState);
473 Q_UNUSED(openMode);
474 return false;
475}
476
477qint64 QBluetoothSocketPrivateBluezDBus::bytesAvailable() const
478{
479 if (localSocket)
480 return localSocket->bytesAvailable();
481
482 return 0;
483}
484
485bool QBluetoothSocketPrivateBluezDBus::canReadLine() const
486{
487 if (localSocket)
488 return localSocket->canReadLine();
489
490 return false;
491}
492
493qint64 QBluetoothSocketPrivateBluezDBus::bytesToWrite() const
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: fd.fileDescriptor());
506 localSocket = new QLocalSocket(this);
507 bool success = localSocket->setSocketDescriptor(
508 socketDescriptor: descriptor, socketState: QLocalSocket::ConnectedState, openMode: q->openMode());
509 if (!success || !localSocket->isValid()) {
510 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
511 delete localSocket;
512 localSocket = nullptr;
513 } else {
514 connect(sender: localSocket, signal: &QLocalSocket::readyRead,
515 context: q, slot: &QBluetoothSocket::readyRead);
516 connect(sender: localSocket, signal: &QLocalSocket::stateChanged,
517 context: this, slot: &QBluetoothSocketPrivateBluezDBus::socketStateChanged);
518 connect(sender: localSocket, signal: &QLocalSocket::bytesWritten,
519 context: q, slot: &QBluetoothSocket::bytesWritten);
520
521 socket = descriptor;
522 q->setSocketState(QBluetoothSocket::SocketState::ConnectedState);
523 }
524}
525
526void QBluetoothSocketPrivateBluezDBus::socketStateChanged(QLocalSocket::LocalSocketState newState)
527{
528 Q_Q(QBluetoothSocket);
529
530 switch (newState) {
531 case QLocalSocket::ClosingState:
532 q->setSocketState(QBluetoothSocket::SocketState::ClosingState);
533 break;
534 case QLocalSocket::UnconnectedState:
535 clearSocket();
536 q->setOpenMode(QIODevice::NotOpen);
537 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
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
564 if (q->state() == QBluetoothSocket::SocketState::ConnectedState) {
565 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
566 QDBusConnection::systemBus());
567 auto reply = device.DisconnectProfile(UUID: 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(profile: QDBusObjectPath(profilePath));
576 reply.waitForFinished();
577 if (reply.isError())
578 qCWarning(QT_BT_BLUEZ) << "Unregister profile:" << reply.error().message();
579
580 QDBusConnection::systemBus().unregisterObject(path: profilePath);
581
582 if (profileContext) {
583 delete profileContext;
584 profileContext = nullptr;
585 }
586
587 remoteDevicePath.clear();
588 profileUuid.clear();
589 profilePath.clear();
590}
591QT_END_NAMESPACE
592
593#include "moc_qbluetoothsocket_bluezdbus_p.cpp"
594

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