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