| 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 | |
| 26 | QT_BEGIN_NAMESPACE |
| 27 | |
| 28 | using namespace Qt::StringLiterals; |
| 29 | using namespace QtBluetoothPrivate; // for D-Bus wrappers |
| 30 | |
| 31 | Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ) |
| 32 | |
| 33 | QBluetoothSocketPrivateBluezDBus::QBluetoothSocketPrivateBluezDBus() |
| 34 | { |
| 35 | secFlags = QBluetooth::Security::NoSecurity; |
| 36 | } |
| 37 | |
| 38 | QBluetoothSocketPrivateBluezDBus::~QBluetoothSocketPrivateBluezDBus() |
| 39 | { |
| 40 | } |
| 41 | |
| 42 | bool 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 | |
| 56 | void 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 | |
| 66 | static 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 | |
| 111 | void 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 | |
| 214 | void 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 | |
| 248 | void 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 | |
| 276 | void 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 | |
| 319 | void 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 | |
| 333 | void 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 | |
| 348 | QString 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 | |
| 360 | QBluetoothAddress 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 | |
| 372 | quint16 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 | |
| 398 | QString 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 | |
| 408 | QBluetoothAddress 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 | |
| 418 | quint16 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 | |
| 444 | qint64 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 | |
| 463 | qint64 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 | |
| 482 | void QBluetoothSocketPrivateBluezDBus::close() |
| 483 | { |
| 484 | abort(); |
| 485 | } |
| 486 | |
| 487 | bool 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 | |
| 497 | qint64 QBluetoothSocketPrivateBluezDBus::bytesAvailable() const |
| 498 | { |
| 499 | if (localSocket) |
| 500 | return localSocket->bytesAvailable(); |
| 501 | |
| 502 | return 0; |
| 503 | } |
| 504 | |
| 505 | bool QBluetoothSocketPrivateBluezDBus::canReadLine() const |
| 506 | { |
| 507 | if (localSocket) |
| 508 | return localSocket->canReadLine(); |
| 509 | |
| 510 | return false; |
| 511 | } |
| 512 | |
| 513 | qint64 QBluetoothSocketPrivateBluezDBus::bytesToWrite() const |
| 514 | { |
| 515 | if (localSocket) |
| 516 | return localSocket->bytesToWrite(); |
| 517 | |
| 518 | return 0; |
| 519 | } |
| 520 | |
| 521 | void 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 | |
| 546 | void 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 | |
| 567 | void 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 | } |
| 619 | QT_END_NAMESPACE |
| 620 | |
| 621 | #include "moc_qbluetoothsocket_bluezdbus_p.cpp" |
| 622 | |