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
37QBluetoothSocketPrivateBluezDBus::~QBluetoothSocketPrivateBluezDBus()
38{
39}
40
41bool QBluetoothSocketPrivateBluezDBus::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
42{
43 switch (type) {
44 case QBluetoothServiceInfo::UnknownProtocol:
45 break;
46 case QBluetoothServiceInfo::RfcommProtocol:
47 case QBluetoothServiceInfo::L2capProtocol:
48 socketType = type;
49 return true;
50 }
51
52 return false;
53}
54
55void QBluetoothSocketPrivateBluezDBus::connectToServiceHelper(
56 const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
57{
58 // TODO Remove when Bluez4 support dropped
59 // Only used by QBluetoothSocketPrivateBluez
60 Q_UNUSED(openMode);
61 Q_UNUSED(address);
62 Q_UNUSED(port);
63}
64
65static QString findRemoteDevicePath(const QBluetoothAddress &address)
66{
67 OrgFreedesktopDBusObjectManagerInterface manager(QStringLiteral("org.bluez"),
68 QStringLiteral("/"),
69 QDBusConnection::systemBus());
70
71 bool ok = false;
72 const QString adapterPath = findAdapterForAddress(wantedAddress: QBluetoothAddress(), ok: &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(s: adapterPath) != 0)
91 continue; // devices whose path does not start with same path we skip
92
93 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"),
94 path.path(), QDBusConnection::systemBus());
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
110void QBluetoothSocketPrivateBluezDBus::connectToServiceHelper(
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(mode: QUuid::WithoutBraces);
119
120 if (!profileManager) {
121 profileManager = new OrgBluezProfileManager1Interface(
122 QStringLiteral("org.bluez"),
123 QStringLiteral("/org/bluez"),
124 QDBusConnection::systemBus(),
125 this);
126 }
127
128 if (profileContext) {
129 qCDebug(QT_BT_BLUEZ) << "Profile context still active. close socket first.";
130 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
131 return;
132 }
133
134
135 profileContext = new OrgBluezProfile1ContextInterface(this);
136 connect(sender: profileContext, signal: &OrgBluezProfile1ContextInterface::newConnection,
137 context: this, slot: &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.
144 arg(a: sanitizeNameForDBus(text: QCoreApplication::applicationName())).
145 arg(a: QCoreApplication::applicationPid()).
146 arg(a: QRandomGenerator::global()->generate());
147
148 success = QDBusConnection::systemBus().registerObject(
149 path: profilePath, object: profileContext, options: 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(s: "Cannot export profile on DBus");
160 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
161
162 return;
163 }
164
165 QVariantMap profileOptions;
166 profileOptions.insert(QStringLiteral("Role"), QStringLiteral("client"));
167 profileOptions.insert(QStringLiteral("Service"), value: profileUuid);
168 profileOptions.insert(QStringLiteral("Name"),
169 QStringLiteral("QBluetoothSocket-%1").arg(a: 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 profile: QDBusObjectPath(profilePath),
178 UUID: profileUuid,
179 options: 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(path: profilePath);
186 errorString = QBluetoothSocket::tr(s: "Cannot register profile on DBus");
187 q->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
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(s: "Cannot find remote device");
198 q->setSocketError(QBluetoothSocket::SocketError::HostNotFoundError);
199 return;
200 }
201
202 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
203 QDBusConnection::systemBus());
204 reply = device.ConnectProfile(UUID: profileUuid);
205 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
206 connect(sender: watcher, signal: &QDBusPendingCallWatcher::finished,
207 context: this, slot: &QBluetoothSocketPrivateBluezDBus::connectToServiceReplyHandler);
208
209 q->setOpenMode(openMode);
210 q->setSocketState(QBluetoothSocket::SocketState::ConnectingState);
211}
212
213void QBluetoothSocketPrivateBluezDBus::connectToServiceReplyHandler(
214 QDBusPendingCallWatcher *watcher)
215{
216 Q_Q(QBluetoothSocket);
217
218 QDBusPendingReply<> reply = *watcher;
219 if (reply.isError()) {
220 qCWarning(QT_BT_BLUEZ) << "Cannot connect to profile/service.";
221
222 clearSocket();
223
224 errorString = QBluetoothSocket::tr(s: "Cannot connect to remote profile");
225 q->setSocketError(QBluetoothSocket::SocketError::HostNotFoundError);
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(profile: QDBusObjectPath(profilePath));
236 reply.waitForFinished();
237 if (reply.isError())
238 qCWarning(QT_BT_BLUEZ) << "Unregister profile:" << reply.error().message();
239
240 QDBusConnection::systemBus().unregisterObject(path: profilePath);
241
242 delete profileManager;
243 profileManager = nullptr;
244 }
245}
246
247void QBluetoothSocketPrivateBluezDBus::connectToService(
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(t: QBluetoothUuid::ServiceClassUuid::SerialPort))
257 targetService = 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(s: "Missing serviceUuid or Serial Port service class uuid");
264 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
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(address: service.device().address(), uuid: targetService, openMode);
273}
274
275void QBluetoothSocketPrivateBluezDBus::connectToService(
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(s: "Invalid Bluetooth address passed to connectToService()");
283 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
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(s: "Missing serviceUuid or Serial Port service class uuid");
291 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
292 return;
293 }
294
295 if (q->state() != QBluetoothSocket::SocketState::UnconnectedState) {
296 qCWarning(QT_BT_BLUEZ) << "QBluetoothSocketPrivateBluezDBus::connectToService called on busy socket";
297 errorString = QBluetoothSocket::tr(s: "Trying to connect while connection is in progress");
298 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
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(s: "Socket type not supported");
306 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
307 return;
308 }
309
310 if (!ensureNativeSocket(type: q->socketType())) {
311 errorString = QBluetoothSocket::tr(s: "Socket type not supported");
312 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
313 return;
314 }
315 connectToServiceHelper(address, uuid, openMode);
316}
317
318void QBluetoothSocketPrivateBluezDBus::connectToService(
319 const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
320{
321
322 Q_UNUSED(port);
323 Q_UNUSED(address);
324 Q_UNUSED(openMode);
325 Q_Q(QBluetoothSocket);
326
327 errorString = tr(s: "Connecting to port is not supported via Bluez DBus");
328 q->setSocketError(QBluetoothSocket::SocketError::ServiceNotFoundError);
329 qCWarning(QT_BT_BLUEZ) << "Connecting to port is not supported (Uuid required)";
330}
331
332void QBluetoothSocketPrivateBluezDBus::abort()
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);
342 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
343 emit q->readChannelFinished();
344 }
345}
346
347QString QBluetoothSocketPrivateBluezDBus::localName() const
348{
349 bool ok = false;
350 const QString adapterPath = findAdapterForAddress(wantedAddress: QBluetoothAddress(), ok: &ok);
351 if (!ok)
352 return QString();
353
354 OrgBluezAdapter1Interface adapter(QStringLiteral("org.bluez"), adapterPath,
355 QDBusConnection::systemBus());
356 return QString(adapter.alias());
357}
358
359QBluetoothAddress QBluetoothSocketPrivateBluezDBus::localAddress() const
360{
361 bool ok = false;
362 const QString adapterPath = findAdapterForAddress(wantedAddress: QBluetoothAddress(), ok: &ok);
363 if (!ok)
364 return QBluetoothAddress();
365
366 OrgBluezAdapter1Interface adapter(QStringLiteral("org.bluez"), adapterPath,
367 QDBusConnection::systemBus());
368 return QBluetoothAddress(adapter.address());
369}
370
371quint16 QBluetoothSocketPrivateBluezDBus::localPort() const
372{
373 int descriptor = -1;
374
375 if (localSocket)
376 descriptor = int(localSocket->socketDescriptor());
377 if (descriptor == -1)
378 return 0;
379
380 if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
381 sockaddr_rc addr;
382 socklen_t addrLength = sizeof(addr);
383
384 if (::getsockname(fd: descriptor, addr: reinterpret_cast<sockaddr *>(&addr), len: &addrLength) == 0)
385 return (addr.rc_channel);
386 } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
387 sockaddr_l2 addr;
388 socklen_t addrLength = sizeof(addr);
389
390 if (::getsockname(fd: descriptor, addr: reinterpret_cast<sockaddr *>(&addr), len: &addrLength) == 0)
391 return addr.l2_psm;
392 }
393
394 return 0;
395}
396
397QString QBluetoothSocketPrivateBluezDBus::peerName() const
398{
399 if (remoteDevicePath.isEmpty())
400 return QString();
401
402 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
403 QDBusConnection::systemBus());
404 return device.alias();
405}
406
407QBluetoothAddress QBluetoothSocketPrivateBluezDBus::peerAddress() const
408{
409 if (remoteDevicePath.isEmpty())
410 return QBluetoothAddress();
411
412 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
413 QDBusConnection::systemBus());
414 return QBluetoothAddress(device.address());
415}
416
417quint16 QBluetoothSocketPrivateBluezDBus::peerPort() const
418{
419 int descriptor = -1;
420
421 if (localSocket)
422 descriptor = int(localSocket->socketDescriptor());
423 if (descriptor == -1)
424 return 0;
425
426 if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
427 sockaddr_rc addr;
428 socklen_t addrLength = sizeof(addr);
429
430 if (::getpeername(fd: descriptor, addr: reinterpret_cast<sockaddr *>(&addr), len: &addrLength) == 0)
431 return addr.rc_channel;
432 } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
433 sockaddr_l2 addr;
434 socklen_t addrLength = sizeof(addr);
435
436 if (::getpeername(fd: descriptor, addr: reinterpret_cast<sockaddr *>(&addr), len: &addrLength) == 0)
437 return addr.l2_psm;
438 }
439
440 return 0;
441}
442
443qint64 QBluetoothSocketPrivateBluezDBus::writeData(const 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 write while not connected");
452 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
453 return -1;
454 }
455
456 if (localSocket)
457 return localSocket->write(data, len: maxSize);
458
459 return -1;
460}
461
462qint64 QBluetoothSocketPrivateBluezDBus::readData(char *data, qint64 maxSize)
463{
464 Q_UNUSED(data);
465 Q_UNUSED(maxSize);
466
467 Q_Q(QBluetoothSocket);
468
469 if (state != QBluetoothSocket::SocketState::ConnectedState) {
470 errorString = QBluetoothSocket::tr(s: "Cannot read while not connected");
471 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
472 return -1;
473 }
474
475 if (localSocket)
476 return localSocket->read(data, maxlen: maxSize);
477
478 return -1;
479}
480
481void QBluetoothSocketPrivateBluezDBus::close()
482{
483 abort();
484}
485
486bool QBluetoothSocketPrivateBluezDBus::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
487 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
488{
489 Q_UNUSED(socketDescriptor);
490 Q_UNUSED(socketType);
491 Q_UNUSED(socketState);
492 Q_UNUSED(openMode);
493 return false;
494}
495
496qint64 QBluetoothSocketPrivateBluezDBus::bytesAvailable() const
497{
498 if (localSocket)
499 return localSocket->bytesAvailable();
500
501 return 0;
502}
503
504bool QBluetoothSocketPrivateBluezDBus::canReadLine() const
505{
506 if (localSocket)
507 return localSocket->canReadLine();
508
509 return false;
510}
511
512qint64 QBluetoothSocketPrivateBluezDBus::bytesToWrite() const
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: fd.fileDescriptor());
525 localSocket = new QLocalSocket(this);
526 bool success = localSocket->setSocketDescriptor(
527 socketDescriptor: descriptor, socketState: QLocalSocket::ConnectedState, openMode: q->openMode());
528 if (!success || !localSocket->isValid()) {
529 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
530 delete localSocket;
531 localSocket = nullptr;
532 } else {
533 connect(sender: localSocket, signal: &QLocalSocket::readyRead,
534 context: q, slot: &QBluetoothSocket::readyRead);
535 connect(sender: localSocket, signal: &QLocalSocket::stateChanged,
536 context: this, slot: &QBluetoothSocketPrivateBluezDBus::socketStateChanged);
537 connect(sender: localSocket, signal: &QLocalSocket::bytesWritten,
538 context: q, slot: &QBluetoothSocket::bytesWritten);
539
540 socket = descriptor;
541 q->setSocketState(QBluetoothSocket::SocketState::ConnectedState);
542 }
543}
544
545void QBluetoothSocketPrivateBluezDBus::socketStateChanged(QLocalSocket::LocalSocketState newState)
546{
547 Q_Q(QBluetoothSocket);
548
549 switch (newState) {
550 case QLocalSocket::ClosingState:
551 q->setSocketState(QBluetoothSocket::SocketState::ClosingState);
552 break;
553 case QLocalSocket::UnconnectedState:
554 clearSocket();
555 q->setOpenMode(QIODevice::NotOpen);
556 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
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
583 if (q->state() == QBluetoothSocket::SocketState::ConnectedState) {
584 OrgBluezDevice1Interface device(QStringLiteral("org.bluez"), remoteDevicePath,
585 QDBusConnection::systemBus());
586 auto reply = device.DisconnectProfile(UUID: 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(profile: QDBusObjectPath(profilePath));
604 reply.waitForFinished();
605 if (reply.isError())
606 qCWarning(QT_BT_BLUEZ) << "Unregister profile:" << reply.error().message();
607
608 QDBusConnection::systemBus().unregisterObject(path: profilePath);
609
610 delete profileManager;
611 profileManager = nullptr;
612 }
613
614 remoteDevicePath.clear();
615 profileUuid.clear();
616 profilePath.clear();
617}
618QT_END_NAMESPACE
619
620#include "moc_qbluetoothsocket_bluezdbus_p.cpp"
621

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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