diff --git a/core/backends/bluetooth/bluetoothdevicelink.cpp b/core/backends/bluetooth/bluetoothdevicelink.cpp index 740a288f..4b03fdd2 100644 --- a/core/backends/bluetooth/bluetoothdevicelink.cpp +++ b/core/backends/bluetooth/bluetoothdevicelink.cpp @@ -1,103 +1,105 @@ /** * Copyright 2016 Saikrishna Arcot * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "bluetoothdevicelink.h" #include "../linkprovider.h" #include "bluetoothlinkprovider.h" #include "bluetoothuploadjob.h" #include "bluetoothdownloadjob.h" #include "core_debug.h" +#include "connectionmultiplexer.h" +#include "multiplexchannel.h" -BluetoothDeviceLink::BluetoothDeviceLink(const QString& deviceId, LinkProvider* parent, QBluetoothSocket* socket) +BluetoothDeviceLink::BluetoothDeviceLink(const QString& deviceId, LinkProvider* parent, ConnectionMultiplexer* connection, QSharedPointer socket) : DeviceLink(deviceId, parent) - , mSocketReader(new DeviceLineReader(socket, this)) - , mBluetoothSocket(socket) + , mSocketReader(new DeviceLineReader(socket.data(), this)) + , mConnection(connection) + , mChannel(socket) , mPairingHandler(new BluetoothPairingHandler(this)) { - connect(mSocketReader, SIGNAL(readyRead()), - this, SLOT(dataReceived())); + connect(mSocketReader, &DeviceLineReader::readyRead, this, &BluetoothDeviceLink::dataReceived); - //We take ownership of the socket. + //We take ownership of the connection. //When the link provider destroys us, //the socket (and the reader) will be //destroyed as well - mBluetoothSocket->setParent(this); - connect(mBluetoothSocket, SIGNAL(disconnected()), this, SLOT(deleteLater())); + mConnection->setParent(this); + connect(socket.data(), &MultiplexChannel::aboutToClose, this, &QObject::deleteLater); } QString BluetoothDeviceLink::name() { return QStringLiteral("BluetoothLink"); // Should be same in both android and kde version } bool BluetoothDeviceLink::sendPacket(NetworkPacket& np) { if (np.hasPayload()) { - BluetoothUploadJob* uploadJob = new BluetoothUploadJob(np.payload(), mBluetoothSocket->peerAddress(), this); + BluetoothUploadJob* uploadJob = new BluetoothUploadJob(np.payload(), mConnection, this); np.setPayloadTransferInfo(uploadJob->transferInfo()); uploadJob->start(); } + //TODO: handle too-big packets int written = mSocketReader->write(np.serialize()); return (written != -1); } void BluetoothDeviceLink::userRequestsPair() { mPairingHandler->requestPairing(); } void BluetoothDeviceLink::userRequestsUnpair() { mPairingHandler->unpair(); } bool BluetoothDeviceLink::linkShouldBeKeptAlive() { return pairStatus() == Paired; } void BluetoothDeviceLink::dataReceived() { if (mSocketReader->bytesAvailable() == 0) return; const QByteArray serializedPacket = mSocketReader->readLine(); //qCDebug(KDECONNECT_CORE) << "BluetoothDeviceLink dataReceived" << packet; NetworkPacket packet((QString())); NetworkPacket::unserialize(serializedPacket, &packet); if (packet.type() == PACKET_TYPE_PAIR) { //TODO: Handle pair/unpair requests and forward them (to the pairing handler?) mPairingHandler->packetReceived(packet); return; } if (packet.hasPayloadTransferInfo()) { - BluetoothDownloadJob* downloadJob = new BluetoothDownloadJob(mBluetoothSocket->peerAddress(), - packet.payloadTransferInfo(), this); + BluetoothDownloadJob* downloadJob = new BluetoothDownloadJob(mConnection, packet.payloadTransferInfo(), this); downloadJob->start(); packet.setPayload(downloadJob->payload(), packet.payloadSize()); } Q_EMIT receivedPacket(packet); if (mSocketReader->bytesAvailable() > 0) { - QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, &BluetoothDeviceLink::dataReceived, Qt::QueuedConnection); } } diff --git a/core/backends/bluetooth/bluetoothdevicelink.h b/core/backends/bluetooth/bluetoothdevicelink.h index b14c3d20..fe5bfc2f 100644 --- a/core/backends/bluetooth/bluetoothdevicelink.h +++ b/core/backends/bluetooth/bluetoothdevicelink.h @@ -1,60 +1,64 @@ /** * Copyright 2016 Saikrishna Arcot * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BLUETOOTHDEVICELINK_H #define BLUETOOTHDEVICELINK_H #include #include #include #include "../devicelink.h" #include "../devicelinereader.h" #include "bluetoothpairinghandler.h" +class ConnectionMultiplexer; +class MultiplexChannel; + class KDECONNECTCORE_EXPORT BluetoothDeviceLink : public DeviceLink { Q_OBJECT public: - BluetoothDeviceLink(const QString& deviceId, LinkProvider* parent, QBluetoothSocket* socket); + BluetoothDeviceLink(const QString& deviceId, LinkProvider* parent, ConnectionMultiplexer* connection, QSharedPointer socket); virtual QString name() override; bool sendPacket(NetworkPacket& np) override; virtual void userRequestsPair() override; virtual void userRequestsUnpair() override; virtual bool linkShouldBeKeptAlive() override; private Q_SLOTS: void dataReceived(); private: DeviceLineReader* mSocketReader; - QBluetoothSocket* mBluetoothSocket; + ConnectionMultiplexer* mConnection; + QSharedPointer mChannel; BluetoothPairingHandler* mPairingHandler; void sendMessage(const QString mMessage); }; #endif diff --git a/core/backends/bluetooth/bluetoothdownloadjob.cpp b/core/backends/bluetooth/bluetoothdownloadjob.cpp index 39e3c2b6..7b17eb51 100644 --- a/core/backends/bluetooth/bluetoothdownloadjob.cpp +++ b/core/backends/bluetooth/bluetoothdownloadjob.cpp @@ -1,41 +1,39 @@ /* * Copyright 2016 Saikrishna Arcot * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "bluetoothdownloadjob.h" +#include "connectionmultiplexer.h" +#include "multiplexchannel.h" -BluetoothDownloadJob::BluetoothDownloadJob(const QBluetoothAddress& remoteAddress, const QVariantMap& transferInfo, QObject* parent) +BluetoothDownloadJob::BluetoothDownloadJob(ConnectionMultiplexer *connection, const QVariantMap& transferInfo, QObject* parent) : QObject(parent) - , mRemoteAddress(remoteAddress) - , mTransferUuid(QBluetoothUuid(transferInfo.value(QStringLiteral("uuid")).toString())) - , mSocket(new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol)) { + QBluetoothUuid id{transferInfo.value(QStringLiteral("uuid")).toString()}; + mSocket = QSharedPointer{connection->getChannel(id).release()}; } QSharedPointer BluetoothDownloadJob::payload() const { return mSocket.staticCast(); } void BluetoothDownloadJob::start() { - connect(mSocket.data(), &QBluetoothSocket::disconnected, mSocket.data(), &QBluetoothSocket::readyRead); - connect(mSocket.data(), &QBluetoothSocket::disconnected, mSocket.data(), &QBluetoothSocket::readChannelFinished); - mSocket->connectToService(mRemoteAddress, mTransferUuid, QIODevice::ReadOnly); } diff --git a/core/backends/bluetooth/bluetoothdownloadjob.h b/core/backends/bluetooth/bluetoothdownloadjob.h index 8b95afa4..3d7bb524 100644 --- a/core/backends/bluetooth/bluetoothdownloadjob.h +++ b/core/backends/bluetooth/bluetoothdownloadjob.h @@ -1,47 +1,48 @@ /* * Copyright 2016 Saikrishna Arcot * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BLUETOOTHDOWNLOADJOB_H #define BLUETOOTHDOWNLOADJOB_H #include #include #include #include #include #include #include +class ConnectionMultiplexer; +class MultiplexChannel; + class BluetoothDownloadJob : public QObject { Q_OBJECT public: - explicit BluetoothDownloadJob(const QBluetoothAddress& remoteAddress, const QVariantMap& transferInfo, QObject* parent = 0); + explicit BluetoothDownloadJob(ConnectionMultiplexer *connection, const QVariantMap& transferInfo, QObject* parent = 0); QSharedPointer payload() const; void start(); private: - QBluetoothAddress mRemoteAddress; - QBluetoothUuid mTransferUuid; - QSharedPointer mSocket; + QSharedPointer mSocket; }; #endif // BLUETOOTHDOWNLOADJOB_H diff --git a/core/backends/bluetooth/bluetoothlinkprovider.cpp b/core/backends/bluetooth/bluetoothlinkprovider.cpp index e69ae0a3..53efd56a 100644 --- a/core/backends/bluetooth/bluetoothlinkprovider.cpp +++ b/core/backends/bluetooth/bluetoothlinkprovider.cpp @@ -1,326 +1,329 @@ /** * Copyright 2016 Saikrishna Arcot * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "bluetoothlinkprovider.h" #include "core_debug.h" +#include "connectionmultiplexer.h" +#include "multiplexchannel.h" #include #include #include #include #include #include #include #include "bluetoothdevicelink.h" BluetoothLinkProvider::BluetoothLinkProvider() { mServiceUuid = QBluetoothUuid(QStringLiteral("185f3df4-3268-4e3f-9fca-d4d5059915bd")); connectTimer = new QTimer(this); connectTimer->setInterval(30000); connectTimer->setSingleShot(false); mServiceDiscoveryAgent = new QBluetoothServiceDiscoveryAgent(this); mServiceDiscoveryAgent->setUuidFilter(mServiceUuid); - connect(mServiceDiscoveryAgent, SIGNAL(finished()), this, SLOT(serviceDiscoveryFinished())); - connect(connectTimer, SIGNAL(timeout()), mServiceDiscoveryAgent, SLOT(start())); + connect(connectTimer, &QTimer::timeout, this, [this]() { + mServiceDiscoveryAgent->start(); + }); + + connect(mServiceDiscoveryAgent, &QBluetoothServiceDiscoveryAgent::serviceDiscovered, this, &BluetoothLinkProvider::serviceDiscovered); } void BluetoothLinkProvider::onStart() { QBluetoothLocalDevice localDevice; if (!localDevice.isValid()) { qCWarning(KDECONNECT_CORE) << "No local bluetooth adapter found"; return; } mBluetoothServer = new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this); mBluetoothServer->setSecurityFlags(QBluetooth::Encryption | QBluetooth::Secure); connect(mBluetoothServer, SIGNAL(newConnection()), this, SLOT(serverNewConnection())); mServiceDiscoveryAgent->start(); connectTimer->start(); mKdeconnectService = mBluetoothServer->listen(mServiceUuid, QStringLiteral("KDE Connect")); } void BluetoothLinkProvider::onStop() { if (!mBluetoothServer) { return; } connectTimer->stop(); mKdeconnectService.unregisterService(); mBluetoothServer->close(); mBluetoothServer->deleteLater(); } //I'm in a new network, let's be polite and introduce myself void BluetoothLinkProvider::onNetworkChange() { } void BluetoothLinkProvider::connectError() { QBluetoothSocket* socket = qobject_cast(sender()); if (!socket) return; qCWarning(KDECONNECT_CORE) << "Couldn't connect to socket:" << socket->errorString(); disconnect(socket, SIGNAL(connected()), this, SLOT(clientConnected())); disconnect(socket, SIGNAL(readyRead()), this, SLOT(serverDataReceived())); disconnect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); mSockets.remove(socket->peerAddress()); socket->deleteLater(); } void BluetoothLinkProvider::addLink(BluetoothDeviceLink* deviceLink, const QString& deviceId) { QMap< QString, DeviceLink* >::iterator oldLinkIterator = mLinks.find(deviceId); if (oldLinkIterator != mLinks.end()) { DeviceLink* oldLink = oldLinkIterator.value(); disconnect(oldLink, SIGNAL(destroyed(QObject*)), this, SLOT(deviceLinkDestroyed(QObject*))); oldLink->deleteLater(); mLinks.erase(oldLinkIterator); } mLinks[deviceId] = deviceLink; } -//I'm the new device and I found an existing device -void BluetoothLinkProvider::serviceDiscoveryFinished() -{ - const QList services = mServiceDiscoveryAgent->discoveredServices(); - - for (QBluetoothServiceInfo info : services) { - if (mSockets.contains(info.device().address())) { - continue; - } +void BluetoothLinkProvider::serviceDiscovered(const QBluetoothServiceInfo& old_info) { + auto info = old_info; + info.setServiceUuid(mServiceUuid); + if (mSockets.contains(info.device().address())) { + return; + } - QBluetoothSocket* socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol); + QBluetoothSocket* socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol); - connect(socket, SIGNAL(connected()), this, SLOT(clientConnected())); - connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); + //Delay before sending data + QPointer deleteableSocket = socket; + connect(socket, &QBluetoothSocket::connected, this, [this,deleteableSocket]() { + QTimer::singleShot(500, this, [this,deleteableSocket]() { + clientConnected(deleteableSocket); + }); + }); + connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); - socket->connectToService(info); + socket->connectToService(info); - qCDebug(KDECONNECT_CORE()) << "Connecting to" << info.device().address(); + qCDebug(KDECONNECT_CORE()) << "Connecting to" << info.device().address(); - if (socket->error() != QBluetoothSocket::NoSocketError) { - qCWarning(KDECONNECT_CORE) << "Socket connection error:" << socket->errorString(); - } + if (socket->error() != QBluetoothSocket::NoSocketError) { + qCWarning(KDECONNECT_CORE) << "Socket connection error:" << socket->errorString(); } } //I'm the new device and I'm connected to the existing device. Time to get data. -void BluetoothLinkProvider::clientConnected() +void BluetoothLinkProvider::clientConnected(QPointer socket) { - QBluetoothSocket* socket = qobject_cast(sender()); if (!socket) return; - qCDebug(KDECONNECT_CORE()) << "Connected to" << socket->peerAddress(); + auto peer = socket->peerAddress(); + + qCDebug(KDECONNECT_CORE()) << "Connected to" << peer; if (mSockets.contains(socket->peerAddress())) { - qCWarning(KDECONNECT_CORE()) << "Duplicate connection to" << socket->peerAddress(); + qCWarning(KDECONNECT_CORE()) << "Duplicate connection to" << peer; socket->close(); socket->deleteLater(); return; } - mSockets.insert(socket->peerAddress(), socket); + ConnectionMultiplexer *multiplexer = new ConnectionMultiplexer(socket, this); - disconnect(socket, SIGNAL(connected()), this, SLOT(clientConnected())); + mSockets.insert(peer, multiplexer); + disconnect(socket, nullptr, this, nullptr); + + auto channel = QSharedPointer{multiplexer->getDefaultChannel().release()}; + connect(channel.data(), &MultiplexChannel::readyRead, this, [this,peer,channel] () { clientIdentityReceived(peer, channel); }); + connect(channel.data(), &MultiplexChannel::aboutToClose, this, [this,peer,channel] () { socketDisconnected(peer, channel.data()); }); - connect(socket, SIGNAL(readyRead()), this, SLOT(clientIdentityReceived())); - connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); + if (channel->bytesAvailable()) clientIdentityReceived(peer, channel); + if (!channel->isOpen()) socketDisconnected(peer, channel.data()); } //I'm the new device and the existing device sent me data. -void BluetoothLinkProvider::clientIdentityReceived() +void BluetoothLinkProvider::clientIdentityReceived(const QBluetoothAddress &peer, QSharedPointer socket) { - QBluetoothSocket* socket = qobject_cast(sender()); - if (!socket) return; + socket->startTransaction(); - QByteArray identityArray; - - if (socket->property("identityArray").isValid()) { - identityArray = socket->property("identityArray").toByteArray(); - } - - while (!identityArray.contains('\n') && socket->bytesAvailable() > 0) { - identityArray += socket->readAll(); - } - if (!identityArray.contains('\n')) { - // This method will get retriggered when more data is available. - socket->setProperty("identityArray", identityArray); + QByteArray identityArray = socket->readLine(); + if (identityArray.isEmpty()) { + socket->rollbackTransaction(); return; } - socket->setProperty("identityArray", QVariant()); + socket->commitTransaction(); - disconnect(socket, SIGNAL(readyRead()), this, SLOT(clientIdentityReceived())); + disconnect(socket.data(), &MultiplexChannel::readyRead, this, nullptr); NetworkPacket receivedPacket; bool success = NetworkPacket::unserialize(identityArray, &receivedPacket); if (!success || receivedPacket.type() != PACKET_TYPE_IDENTITY) { qCWarning(KDECONNECT_CORE) << "Not an identity packet"; - mSockets.remove(socket->peerAddress()); + mSockets.remove(peer); socket->close(); socket->deleteLater(); return; } - qCDebug(KDECONNECT_CORE()) << "Received identity packet from" << socket->peerAddress(); + qCDebug(KDECONNECT_CORE()) << "Received identity packet from" << peer; - disconnect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); + //TODO? + //disconnect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); const QString& deviceId = receivedPacket.get(QStringLiteral("deviceId")); - BluetoothDeviceLink* deviceLink = new BluetoothDeviceLink(deviceId, this, socket); + BluetoothDeviceLink* deviceLink = new BluetoothDeviceLink(deviceId, this, mSockets[peer], socket); NetworkPacket np2; NetworkPacket::createIdentityPacket(&np2); success = deviceLink->sendPacket(np2); if (success) { qCDebug(KDECONNECT_CORE) << "Handshaking done (I'm the new device)"; connect(deviceLink, SIGNAL(destroyed(QObject*)), this, SLOT(deviceLinkDestroyed(QObject*))); Q_EMIT onConnectionReceived(receivedPacket, deviceLink); //We kill any possible link from this same device addLink(deviceLink, deviceId); } else { // Connection might be lost. Delete it. delete deviceLink; } //We don't delete the socket because now it's owned by the BluetoothDeviceLink } //I'm the existing device, a new device is kindly introducing itself. void BluetoothLinkProvider::serverNewConnection() { QBluetoothSocket* socket = mBluetoothServer->nextPendingConnection(); qCDebug(KDECONNECT_CORE()) << "Received connection from" << socket->peerAddress(); - if (mSockets.contains(socket->peerAddress())) { - qCDebug(KDECONNECT_CORE()) << "Duplicate connection from" << socket->peerAddress(); + QBluetoothAddress peer = socket->peerAddress(); + + if (mSockets.contains(peer)) { + qCDebug(KDECONNECT_CORE()) << "Duplicate connection from" << peer; socket->close(); socket->deleteLater(); return; } - connect(socket, SIGNAL(readyRead()), this, SLOT(serverDataReceived())); - connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); - connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); + ConnectionMultiplexer *multiplexer = new ConnectionMultiplexer(socket, this); + + mSockets.insert(peer, multiplexer); + disconnect(socket, nullptr, this, nullptr); + + auto channel = QSharedPointer{multiplexer->getDefaultChannel().release()}; + connect(channel.data(), &MultiplexChannel::readyRead, this, [this,peer,channel] () { serverDataReceived(peer, channel); }); + connect(channel.data(), &MultiplexChannel::aboutToClose, this, [this,peer,channel] () { socketDisconnected(peer, channel.data()); }); + + if (!channel->isOpen()) { + socketDisconnected(peer, channel.data()); + return; + } NetworkPacket np2; NetworkPacket::createIdentityPacket(&np2); socket->write(np2.serialize()); qCDebug(KDECONNECT_CORE()) << "Sent identity packet to" << socket->peerAddress(); - - mSockets.insert(socket->peerAddress(), socket); } //I'm the existing device and this is the answer to my identity packet (data received) -void BluetoothLinkProvider::serverDataReceived() +void BluetoothLinkProvider::serverDataReceived(const QBluetoothAddress &peer, QSharedPointer socket) { - QBluetoothSocket* socket = qobject_cast(sender()); - if (!socket) return; - QByteArray identityArray; - if (socket->property("identityArray").isValid()) { - identityArray = socket->property("identityArray").toByteArray(); - } + socket->startTransaction(); + identityArray = socket->readLine(); - while (!identityArray.contains('\n') && socket->bytesAvailable() > 0) { - identityArray += socket->readAll(); - } - if (!identityArray.contains('\n')) { - // This method will get retriggered when more data is available. - socket->setProperty("identityArray", identityArray); + if (identityArray.isEmpty()) { + socket->rollbackTransaction(); return; } - socket->setProperty("identityArray", QVariant()); + socket->commitTransaction(); - disconnect(socket, SIGNAL(readyRead()), this, SLOT(serverDataReceived())); - disconnect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(connectError())); + disconnect(socket.data(), &MultiplexChannel::readyRead, this, nullptr); NetworkPacket receivedPacket; bool success = NetworkPacket::unserialize(identityArray, &receivedPacket); if (!success || receivedPacket.type() != PACKET_TYPE_IDENTITY) { qCWarning(KDECONNECT_CORE) << "Not an identity packet."; - mSockets.remove(socket->peerAddress()); + mSockets.remove(peer); socket->close(); socket->deleteLater(); return; } - qCDebug(KDECONNECT_CORE()) << "Received identity packet from" << socket->peerAddress(); + qCDebug(KDECONNECT_CORE()) << "Received identity packet from" << peer; const QString& deviceId = receivedPacket.get(QStringLiteral("deviceId")); - BluetoothDeviceLink* deviceLink = new BluetoothDeviceLink(deviceId, this, socket); + BluetoothDeviceLink* deviceLink = new BluetoothDeviceLink(deviceId, this, mSockets[peer], socket); connect(deviceLink, SIGNAL(destroyed(QObject*)), this, SLOT(deviceLinkDestroyed(QObject*))); Q_EMIT onConnectionReceived(receivedPacket, deviceLink); addLink(deviceLink, deviceId); } void BluetoothLinkProvider::deviceLinkDestroyed(QObject* destroyedDeviceLink) { const QString id = destroyedDeviceLink->property("deviceId").toString(); qCDebug(KDECONNECT_CORE()) << "Device disconnected:" << id; QMap< QString, DeviceLink* >::iterator oldLinkIterator = mLinks.find(id); if (oldLinkIterator != mLinks.end() && oldLinkIterator.value() == destroyedDeviceLink) { mLinks.erase(oldLinkIterator); } } -void BluetoothLinkProvider::socketDisconnected() +void BluetoothLinkProvider::socketDisconnected(const QBluetoothAddress &peer, MultiplexChannel *socket) { - QBluetoothSocket* socket = qobject_cast(sender()); - if (!socket) return; - - disconnect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); + qCDebug(KDECONNECT_CORE()) << "Disconnected"; + disconnect(socket, nullptr, this, nullptr); - mSockets.remove(mSockets.key(socket)); + mSockets.remove(peer); } BluetoothLinkProvider::~BluetoothLinkProvider() { } diff --git a/core/backends/bluetooth/bluetoothlinkprovider.h b/core/backends/bluetooth/bluetoothlinkprovider.h index 744ce573..ad0a2278 100644 --- a/core/backends/bluetooth/bluetoothlinkprovider.h +++ b/core/backends/bluetooth/bluetoothlinkprovider.h @@ -1,83 +1,86 @@ /** * Copyright 2016 Saikrishna Arcot * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BLUETOOTHLINKPROVIDER_H #define BLUETOOTHLINKPROVIDER_H #include #include #include #include #include #include #include #include #include #include #include "../linkprovider.h" class BluetoothDeviceLink; +class ConnectionMultiplexer; +class MultiplexChannel; class KDECONNECTCORE_EXPORT BluetoothLinkProvider : public LinkProvider { Q_OBJECT public: BluetoothLinkProvider(); virtual ~BluetoothLinkProvider(); QString name() override { return QStringLiteral("BluetoothLinkProvider"); } int priority() override { return PRIORITY_MEDIUM; } public Q_SLOTS: virtual void onNetworkChange() override; virtual void onStart() override; virtual void onStop() override; void connectError(); - void serviceDiscoveryFinished(); private Q_SLOTS: void deviceLinkDestroyed(QObject* destroyedDeviceLink); - void socketDisconnected(); + void socketDisconnected(const QBluetoothAddress &peerAddress, MultiplexChannel *socket); void serverNewConnection(); - void serverDataReceived(); - void clientConnected(); - void clientIdentityReceived(); + void serverDataReceived(const QBluetoothAddress &peerAddress, QSharedPointer socket); + void clientConnected(QPointer socket); + void clientIdentityReceived(const QBluetoothAddress &peerAddress, QSharedPointer socket); + + void serviceDiscovered(const QBluetoothServiceInfo &info); private: void addLink(BluetoothDeviceLink* deviceLink, const QString& deviceId); QList getPairedDevices(); QBluetoothUuid mServiceUuid; QPointer mBluetoothServer; QBluetoothServiceInfo mKdeconnectService; QBluetoothServiceDiscoveryAgent* mServiceDiscoveryAgent; QTimer* connectTimer; QMap mLinks; - QMap mSockets; + QMap mSockets; }; #endif diff --git a/core/backends/bluetooth/bluetoothuploadjob.cpp b/core/backends/bluetooth/bluetoothuploadjob.cpp index f8cfaec1..3f378704 100644 --- a/core/backends/bluetooth/bluetoothuploadjob.cpp +++ b/core/backends/bluetooth/bluetoothuploadjob.cpp @@ -1,113 +1,78 @@ /* * Copyright 2016 Saikrishna Arcot * Copyright 2018 Matthijs TIjink * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "bluetoothuploadjob.h" +#include "connectionmultiplexer.h" +#include "multiplexchannel.h" #include #include "core_debug.h" #include -BluetoothUploadJob::BluetoothUploadJob(const QSharedPointer& data, const QBluetoothAddress& remoteAddress, QObject* parent) +BluetoothUploadJob::BluetoothUploadJob(const QSharedPointer& data, ConnectionMultiplexer *connection, QObject* parent) : QObject(parent) , mData(data) - , mRemoteAddress(remoteAddress) - , mTransferUuid(QBluetoothUuid::createUuid()) - , mServer(new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this)) + , mTransferUuid(connection->newChannel()) { - mServer->setSecurityFlags(QBluetooth::Encryption | QBluetooth::Secure); + mSocket = QSharedPointer{connection->getChannel(mTransferUuid).release()}; } QVariantMap BluetoothUploadJob::transferInfo() const { QVariantMap ret; ret[QStringLiteral("uuid")] = mTransferUuid.toString().mid(1, 36); return ret; } void BluetoothUploadJob::start() { - connect(mServer, &QBluetoothServer::newConnection, this, &BluetoothUploadJob::newConnection); - mServiceInfo = mServer->listen(mTransferUuid, QStringLiteral("KDE Connect Transfer Job")); - Q_ASSERT(mServiceInfo.isValid()); -} - -void BluetoothUploadJob::newConnection() -{ - m_socket = mServer->nextPendingConnection(); - Q_ASSERT(m_socket); - m_socket->setParent(this); - connect(m_socket, &QBluetoothSocket::disconnected, this, &BluetoothUploadJob::deleteLater); - - if (m_socket->peerAddress() != mRemoteAddress) { - m_socket->close(); - } else { - mServer->close(); - disconnect(mServer, &QBluetoothServer::newConnection, this, &BluetoothUploadJob::newConnection); - mServiceInfo.unregisterService(); - - if (!mData->open(QIODevice::ReadOnly)) { - qCWarning(KDECONNECT_CORE) << "error when opening the input to upload"; - m_socket->close(); - deleteLater(); - return; - } + if (!mData->open(QIODevice::ReadOnly)) { + qCWarning(KDECONNECT_CORE) << "error when opening the input to upload"; + return; //TODO: Handle error, clean up... } - - connect(m_socket, &QBluetoothSocket::bytesWritten, this, &BluetoothUploadJob::writeSome); - connect(m_socket, &QBluetoothSocket::disconnected, this, &BluetoothUploadJob::closeConnection); + connect(mSocket.data(), &MultiplexChannel::bytesWritten, this, &BluetoothUploadJob::writeSome); + connect(mSocket.data(), &MultiplexChannel::aboutToClose, this, &BluetoothUploadJob::closeConnection); writeSome(); } void BluetoothUploadJob::writeSome() { - Q_ASSERT(m_socket); - bool errorOccurred = false; - while (m_socket->bytesToWrite() == 0 && mData->bytesAvailable() && m_socket->isWritable()) { + while (mSocket->bytesToWrite() == 0 && mData->bytesAvailable() && mSocket->isWritable()) { qint64 bytes = qMin(mData->bytesAvailable(), 4096); - int bytesWritten = m_socket->write(mData->read(bytes)); + int bytesWritten = mSocket->write(mData->read(bytes)); if (bytesWritten < 0) { qCWarning(KDECONNECT_CORE()) << "error when writing data to bluetooth upload" << bytes << mData->bytesAvailable(); errorOccurred = true; break; } } if (mData->atEnd() || errorOccurred) { - disconnect(m_socket, &QBluetoothSocket::bytesWritten, this, &BluetoothUploadJob::writeSome); mData->close(); - - connect(m_socket, &QBluetoothSocket::bytesWritten, this, &BluetoothUploadJob::finishWrites); - finishWrites(); - } -} - -void BluetoothUploadJob::finishWrites() { - Q_ASSERT(m_socket); - if (m_socket->bytesToWrite() == 0) { - closeConnection(); + mSocket->close(); } } void BluetoothUploadJob::closeConnection() { mData->close(); deleteLater(); } diff --git a/core/backends/bluetooth/bluetoothuploadjob.h b/core/backends/bluetooth/bluetoothuploadjob.h index a0a53692..04c9f754 100644 --- a/core/backends/bluetooth/bluetoothuploadjob.h +++ b/core/backends/bluetooth/bluetoothuploadjob.h @@ -1,59 +1,57 @@ /* * Copyright 2016 Saikrishna Arcot * Copyright 2018 Matthijs TIjink * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BLUETOOTHUPLOADJOB_H #define BLUETOOTHUPLOADJOB_H #include #include #include #include #include #include #include +class ConnectionMultiplexer; +class MultiplexChannel; + class BluetoothUploadJob : public QObject { Q_OBJECT public: - explicit BluetoothUploadJob(const QSharedPointer& data, const QBluetoothAddress& remoteAddress, QObject* parent = 0); + explicit BluetoothUploadJob(const QSharedPointer& data, ConnectionMultiplexer *connection, QObject* parent = 0); QVariantMap transferInfo() const; void start(); private: QSharedPointer mData; - QBluetoothAddress mRemoteAddress; QBluetoothUuid mTransferUuid; - QBluetoothServer* mServer; - QBluetoothServiceInfo mServiceInfo; - QBluetoothSocket* m_socket; + QSharedPointer mSocket; void closeConnection(); private Q_SLOTS: - void newConnection(); void writeSome(); - void finishWrites(); }; #endif // BLUETOOTHUPLOADJOB_H diff --git a/core/backends/bluetooth/connectionmultiplexer.cpp b/core/backends/bluetooth/connectionmultiplexer.cpp index fc532cf6..18344f4c 100644 --- a/core/backends/bluetooth/connectionmultiplexer.cpp +++ b/core/backends/bluetooth/connectionmultiplexer.cpp @@ -1,401 +1,401 @@ /** * Copyright 2019 Matthijs Tijink * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "connectionmultiplexer.h" #include #include #include #include #include "multiplexchannel.h" #include "core_debug.h" #include "multiplexchannelstate.h" /** * The default channel uuid. This channel is opened implicitely (without communication). */ -constexpr const char DEFAULT_CHANNEL_UUID[] = "a0d0aaf4-1072-4d81-aa35-902a954b1266"; +#define DEFAULT_CHANNEL_UUID "a0d0aaf4-1072-4d81-aa35-902a954b1266" //Message type constants constexpr char MESSAGE_PROTOCOL_VERSION = 0; constexpr char MESSAGE_OPEN_CHANNEL = 1; constexpr char MESSAGE_CLOSE_CHANNEL = 2; constexpr char MESSAGE_READ = 3; constexpr char MESSAGE_WRITE = 4; ConnectionMultiplexer::ConnectionMultiplexer(QBluetoothSocket *socket, QObject *parent) : QObject(parent), mSocket{socket}, receivedProtocolVersion{false} { connect(mSocket, &QIODevice::readyRead, this, &ConnectionMultiplexer::readyRead); connect(mSocket, &QIODevice::aboutToClose, this, &ConnectionMultiplexer::disconnected); connect(mSocket, &QBluetoothSocket::disconnected, this, &ConnectionMultiplexer::disconnected); connect(mSocket, &QIODevice::bytesWritten, this, &ConnectionMultiplexer::bytesWritten); //Send the protocol version QByteArray message(23, (char) 0); message[0] = MESSAGE_PROTOCOL_VERSION; qToBigEndian(4, &message.data()[1]); //Leave UUID empty //Only support version 1 (lowest supported = highest supported = 1) qToBigEndian(1, &message.data()[19]); qToBigEndian(1, &message.data()[21]); to_write_bytes.append(message); //Send the protocol version message (queued) QMetaObject::invokeMethod(this, &ConnectionMultiplexer::bytesWritten, Qt::QueuedConnection); //Always open the default channel - addChannel(QBluetoothUuid{QString{DEFAULT_CHANNEL_UUID}}); + addChannel(QBluetoothUuid{QStringLiteral(DEFAULT_CHANNEL_UUID)}); //Immediately check if we can read stuff ("readyRead" may not be called in that case) if (mSocket->bytesAvailable()) { //But invoke it queued QMetaObject::invokeMethod(this, &ConnectionMultiplexer::readyRead, Qt::QueuedConnection); } } ConnectionMultiplexer::~ConnectionMultiplexer() { //Always make sure we close the connection close(); } void ConnectionMultiplexer::readyRead() { //Continue parsing messages until we need more data for another message while (tryParseMessage()) {} } void ConnectionMultiplexer::disconnected() { //In case we get disconnected, remove all channels for (auto &&channel : channels) { disconnect(channel.data(), nullptr, this, nullptr); channel->disconnected(); } channels.clear(); for (auto channel : unrequested_channels) { delete channel; } unrequested_channels.clear(); } void ConnectionMultiplexer::close() { //In case we want to close the connection, remove all channels for (auto &&channel : channels) { disconnect(channel.data(), nullptr, this, nullptr); channel->disconnected(); } channels.clear(); for (auto channel : unrequested_channels) { delete channel; } unrequested_channels.clear(); mSocket->close(); } bool ConnectionMultiplexer::isOpen() const { return mSocket->isOpen(); } bool ConnectionMultiplexer::tryParseMessage() { mSocket->startTransaction(); //The message header is 19 bytes long QByteArray header = mSocket->read(19); if (header.size() != 19) { mSocket->rollbackTransaction(); return false; } /** * Parse the header: * - message type (1 byte) * - message length (2 bytes, Big-Endian), excludes header size * - channel uuid (16 bytes, Big-Endian) */ char message_type = header[0]; uint16_t message_length = qFromBigEndian(&header.data()[1]); quint128 message_uuid_raw; for (int i = 0; i < 16; ++i) message_uuid_raw.data[i] = header[3 + i]; QBluetoothUuid message_uuid = QBluetoothUuid(message_uuid_raw); //Check if we have the full message including its data QByteArray data = mSocket->read(message_length); if (data.size() != message_length) { mSocket->rollbackTransaction(); return false; } Q_ASSERT(receivedProtocolVersion || message_type == MESSAGE_PROTOCOL_VERSION); //Parse the different message types if (message_type == MESSAGE_OPEN_CHANNEL) { //The other endpoint requested us to open a channel Q_ASSERT(message_length == 0); addChannel(message_uuid); } else if (message_type == MESSAGE_READ) { //The other endpoint has read some data and requests more data Q_ASSERT(message_length == 2); //Read the number of bytes requested (2 bytes, Big-Endian) uint16_t additional_read = qFromBigEndian(data.data()); Q_ASSERT(additional_read > 0); //Check if we haven't closed the channel in the meanwhile // (note: different from the user's endpoint of a closed channel, since we might have outstanding buffers) auto iter = channels.find(message_uuid); if (iter != channels.end() && (*iter)->connected) { auto channel = *iter; //We have "additional_read" more bytes we can safely write in this channel channel->freeWriteAmount += additional_read; mSocket->commitTransaction(); //We might still have some data in the write buffer Q_EMIT channel->writeAvailable(); return true; } } else if (message_type == MESSAGE_WRITE) { //The other endpoint has written data into a channel (because we requested it) Q_ASSERT(message_length > 0); //Check if we haven't closed the channel in the meanwhile // (note: different from the user's endpoint of a closed channel, since we might have outstanding buffers) auto iter = channels.find(message_uuid); if (iter != channels.end() && (*iter)->connected) { auto channel = *iter; Q_ASSERT(channel->requestedReadAmount >= message_length); //We received some data, so update the buffer and the amount of outstanding read requests channel->requestedReadAmount -= message_length; channel->read_buffer.append(std::move(data)); mSocket->commitTransaction(); //Indicate that the channel can read some bytes Q_EMIT channel->readyRead(); return true; } } else if (message_type == MESSAGE_CLOSE_CHANNEL) { //The other endpoint wants to close a channel Q_ASSERT(message_length == 0); //Check if we haven't closed the channel in the meanwhile // (note: different from the user's endpoint of a closed channel, since we might have outstanding buffers) auto iter = channels.find(message_uuid); if (iter != channels.end()) { auto channel = *iter; //We don't want signals anymore, since the channel is closed disconnect(channel.data(), nullptr, this, nullptr); removeChannel(message_uuid); } } else if (message_type == MESSAGE_PROTOCOL_VERSION) { //Checks for protocol compatibility Q_ASSERT(message_length >= 4); //Read the lowest & highest version supported (each 2 bytes, Big-Endian) uint16_t lowest_version = qFromBigEndian(&data.data()[0]); uint16_t highest_version = qFromBigEndian(&data.data()[2]); Q_ASSERT(lowest_version == 1); Q_ASSERT(highest_version >= 1); receivedProtocolVersion = true; } else { //Other message types are not supported Q_ASSERT(false); } mSocket->commitTransaction(); return true; } QBluetoothUuid ConnectionMultiplexer::newChannel() { //Create a random uuid QBluetoothUuid new_id(QUuid::createUuid()); //Open the channel on the other endpoint QByteArray message(3, (char) 0); message[0] = MESSAGE_OPEN_CHANNEL; qToBigEndian(0, &message.data()[1]); quint128 new_id_raw = new_id.toUInt128(); message.append((const char*) new_id_raw.data, 16); to_write_bytes.append(message); //Add the channel ourselves addChannel(new_id); //Write the data bytesWritten(); return new_id; } void ConnectionMultiplexer::addChannel(QBluetoothUuid new_id) { MultiplexChannelState *channelState = new MultiplexChannelState(); //Connect all channels queued, so that we have opportunities to combine read/write requests Q_ASSERT(unrequested_channels.size() <= 20); //Note that none of the channels knows its own uuid, so we have to add it ourselves connect(channelState, &MultiplexChannelState::readAvailable, this, [new_id,this] () { channelCanRead(new_id); }, Qt::QueuedConnection); connect(channelState, &MultiplexChannelState::writeAvailable, this, [new_id,this] () { channelCanWrite(new_id); }, Qt::QueuedConnection); connect(channelState, &MultiplexChannelState::requestClose, this, [new_id,this] () { closeChannel(new_id); }, Qt::QueuedConnection); auto channelStatePtr = QSharedPointer{channelState}; channels[new_id] = channelStatePtr; unrequested_channels[new_id] = new MultiplexChannel{channelStatePtr}; //Immediately ask for data in this channel Q_EMIT channelStatePtr->readAvailable(); } std::unique_ptr ConnectionMultiplexer::getChannel(QBluetoothUuid channelId) { auto iter = unrequested_channels.find(channelId); if (iter == unrequested_channels.end()) { return nullptr; } else if (!(*iter)->isOpen()) { //Delete the channel delete *iter; unrequested_channels.erase(iter); //Don't return closed channels return nullptr; } else { auto channel = *iter; unrequested_channels.erase(iter); return std::unique_ptr{channel}; } } std::unique_ptr ConnectionMultiplexer::getDefaultChannel() { - return getChannel(QBluetoothUuid{QString{DEFAULT_CHANNEL_UUID}}); + return getChannel(QBluetoothUuid{QStringLiteral(DEFAULT_CHANNEL_UUID)}); } void ConnectionMultiplexer::bytesWritten() { if (to_write_bytes.size() > 0) { //If we have stuff to write, try to write it auto num_written = mSocket->write(to_write_bytes); if (num_written <= 0) { //On error: disconnected will be called later //On buffer full: will be retried later return; } else if (num_written == to_write_bytes.size()) { to_write_bytes.clear(); } else { to_write_bytes.remove(0, num_written); return; } } } void ConnectionMultiplexer::channelCanRead(QBluetoothUuid channelId) { auto iter = channels.find(channelId); if (iter == channels.end()) return; auto channel = *iter; //Check if we can request more data to read without overflowing the buffer if (channel->read_buffer.size() + channel->requestedReadAmount < channel->BUFFER_SIZE) { //Request the exact amount to fill up the buffer auto read_amount = channel->BUFFER_SIZE - channel->requestedReadAmount - channel->read_buffer.size(); channel->requestedReadAmount += read_amount; //Send a MESSAGE_READ request for more data QByteArray message(3, (char) 0); message[0] = MESSAGE_READ; qToBigEndian(2, &message.data()[1]); quint128 id_raw = channelId.toUInt128(); message.append((const char*) id_raw.data, 16); message.append(2, 0); qToBigEndian(read_amount, &message.data()[19]); to_write_bytes.append(message); //Try to send it immediately bytesWritten(); } } void ConnectionMultiplexer::channelCanWrite(QBluetoothUuid channelId) { auto iter = channels.find(channelId); if (iter == channels.end()) return; auto channel = *iter; //Check if we can freely send data and we actually have some data if (channel->write_buffer.size() > 0 && channel->freeWriteAmount > 0) { //Figure out how much we can send now auto amount = qMin((int) channel->write_buffer.size(), channel->freeWriteAmount); QByteArray data = channel->write_buffer.left(amount); channel->write_buffer.remove(0, amount); channel->freeWriteAmount -= amount; //Send the data QByteArray message(3, (char) 0); message[0] = MESSAGE_WRITE; qToBigEndian(amount, &message.data()[1]); quint128 id_raw = channelId.toUInt128(); message.append((const char*) id_raw.data, 16); message.append(data); to_write_bytes.append(message); //Try to send it immediately bytesWritten(); //Let the channel's users know that some data has been written Q_EMIT channel->bytesWritten(amount); //If the user previously asked to close the channel and we finally managed to write the buffer, actually close it if (channel->write_buffer.isEmpty() && channel->close_after_write) { closeChannel(channelId); } } } void ConnectionMultiplexer::closeChannel(QBluetoothUuid channelId) { auto iter = channels.find(channelId); if (iter == channels.end()) return; auto channel = *iter; //If the user wants to close a channel, then the user won't be reading from it anymore channel->read_buffer.clear(); channel->close_after_write = true; //If there's still stuff to write, don't close it just yet if (!channel->write_buffer.isEmpty()) return; channels.erase(iter); channel->connected = false; //Send the actual close channel message QByteArray message(3, (char) 0); message[0] = MESSAGE_CLOSE_CHANNEL; qToBigEndian(0, &message.data()[1]); quint128 id_raw = channelId.toUInt128(); message.append((const char*) id_raw.data, 16); to_write_bytes.append(message); //Try to send it immediately bytesWritten(); } void ConnectionMultiplexer::removeChannel(QBluetoothUuid channelId) { auto iter = channels.find(channelId); if (iter == channels.end()) return; auto channel = *iter; //Remove the channel from the channel list channels.erase(iter); channel->connected = false; Q_EMIT channel->disconnected(); } diff --git a/core/backends/devicelinereader.cpp b/core/backends/devicelinereader.cpp index 0c9f463f..b9918e7f 100644 --- a/core/backends/devicelinereader.cpp +++ b/core/backends/devicelinereader.cpp @@ -1,55 +1,53 @@ /** * Copyright 2013 Albert Vaca * Copyright 2014 Alejandro Fiestas Olivares * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "devicelinereader.h" DeviceLineReader::DeviceLineReader(QIODevice* device, QObject* parent) : QObject(parent) , m_device(device) { - connect(m_device, SIGNAL(readyRead()), - this, SLOT(dataReceived())); - connect(m_device, SIGNAL(disconnected()), - this, SIGNAL(disconnected())); + connect(m_device, &QIODevice::readyRead, this, &DeviceLineReader::dataReceived); + connect(m_device, &QIODevice::aboutToClose, this, &DeviceLineReader::disconnected); } void DeviceLineReader::dataReceived() { while(m_device->canReadLine()) { const QByteArray line = m_device->readLine(); if (line.length() > 1) { m_packets.enqueue(line);//we don't want single \n } } //If we still have things to read from the device, call dataReceived again //We do this manually because we do not trust readyRead to be emitted again //So we call this method again just in case. if (m_device->bytesAvailable() > 0) { QMetaObject::invokeMethod(this, "dataReceived", Qt::QueuedConnection); return; } //If we have any packets, tell it to the world. if (!m_packets.isEmpty()) { Q_EMIT readyRead(); } }