File indexing completed on 2024-05-12 04:58:51

0001 /**
0002  * SPDX-FileCopyrightText: 2019 Matthijs Tijink <matthijstijink@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "connectionmultiplexer.h"
0008 
0009 #include "core_debug.h"
0010 #include "multiplexchannel.h"
0011 #include "multiplexchannelstate.h"
0012 #include <QBluetoothSocket>
0013 #include <QBluetoothUuid>
0014 #include <QIODevice>
0015 #include <QtEndian>
0016 
0017 /**
0018  * The default channel uuid. This channel is opened implicitly (without communication).
0019  */
0020 #define DEFAULT_CHANNEL_UUID "a0d0aaf4-1072-4d81-aa35-902a954b1266"
0021 
0022 // Message type constants
0023 constexpr char MESSAGE_PROTOCOL_VERSION = 0;
0024 constexpr char MESSAGE_OPEN_CHANNEL = 1;
0025 constexpr char MESSAGE_CLOSE_CHANNEL = 2;
0026 constexpr char MESSAGE_READ = 3;
0027 constexpr char MESSAGE_WRITE = 4;
0028 
0029 ConnectionMultiplexer::ConnectionMultiplexer(QBluetoothSocket *socket, QObject *parent)
0030     : QObject(parent)
0031     , mSocket{socket}
0032     , receivedProtocolVersion{false}
0033 {
0034     connect(mSocket, &QIODevice::readyRead, this, &ConnectionMultiplexer::readyRead);
0035     connect(mSocket, &QIODevice::aboutToClose, this, &ConnectionMultiplexer::disconnected);
0036     connect(mSocket, &QBluetoothSocket::disconnected, this, &ConnectionMultiplexer::disconnected);
0037     connect(mSocket, &QIODevice::bytesWritten, this, &ConnectionMultiplexer::bytesWritten);
0038 
0039     // Send the protocol version
0040     QByteArray message(23, (char)0);
0041     message[0] = MESSAGE_PROTOCOL_VERSION;
0042     qToBigEndian<uint16_t>(4, &message.data()[1]);
0043     // Leave UUID empty
0044     // Only support version 1 (lowest supported = highest supported = 1)
0045     qToBigEndian<uint16_t>(1, &message.data()[19]);
0046     qToBigEndian<uint16_t>(1, &message.data()[21]);
0047 
0048     socket->write(message);
0049 
0050     // Send the protocol version message (queued)
0051     QMetaObject::invokeMethod(this, &ConnectionMultiplexer::bytesWritten, Qt::QueuedConnection);
0052 
0053     // Always open the default channel
0054     addChannel(QBluetoothUuid{QStringLiteral(DEFAULT_CHANNEL_UUID)});
0055 
0056     // Immediately check if we can read stuff ("readyRead" may not be called in that case)
0057     if (mSocket->bytesAvailable()) {
0058         // But invoke it queued
0059         QMetaObject::invokeMethod(this, &ConnectionMultiplexer::readyRead, Qt::QueuedConnection);
0060     }
0061 }
0062 
0063 ConnectionMultiplexer::~ConnectionMultiplexer()
0064 {
0065     // Always make sure we close the connection
0066     close();
0067 }
0068 
0069 void ConnectionMultiplexer::readyRead()
0070 {
0071     // Continue parsing messages until we need more data for another message
0072     while (tryParseMessage()) { }
0073 }
0074 
0075 void ConnectionMultiplexer::disconnected()
0076 {
0077     // In case we get disconnected, remove all channels
0078     for (auto &&channel : channels) {
0079         disconnect(channel.data(), nullptr, this, nullptr);
0080         channel->disconnected();
0081     }
0082     channels.clear();
0083     for (auto channel : unrequested_channels) {
0084         delete channel;
0085     }
0086     unrequested_channels.clear();
0087 }
0088 
0089 void ConnectionMultiplexer::close()
0090 {
0091     // In case we want to close the connection, remove all channels
0092     for (auto &&channel : channels) {
0093         disconnect(channel.data(), nullptr, this, nullptr);
0094         channel->disconnected();
0095     }
0096     channels.clear();
0097     for (auto channel : unrequested_channels) {
0098         delete channel;
0099     }
0100     unrequested_channels.clear();
0101 
0102     mSocket->close();
0103 }
0104 
0105 bool ConnectionMultiplexer::isOpen() const
0106 {
0107     return mSocket->isOpen();
0108 }
0109 
0110 bool ConnectionMultiplexer::tryParseMessage()
0111 {
0112     mSocket->startTransaction();
0113 
0114     // The message header is 19 bytes long
0115     QByteArray header = mSocket->read(19);
0116     if (header.size() != 19) {
0117         mSocket->rollbackTransaction();
0118         return false;
0119     }
0120 
0121     /**
0122      * Parse the header:
0123      *  - message type (1 byte)
0124      *  - message length (2 bytes, Big-Endian), excludes header size
0125      *  - channel uuid (16 bytes, Big-Endian)
0126      */
0127     char message_type = header[0];
0128     uint16_t message_length = qFromBigEndian<uint16_t>(&header.data()[1]);
0129 
0130     quint128 message_uuid_raw;
0131 #ifndef QT_SUPPORTS_INT128
0132     for (int i = 0; i < 16; ++i) {
0133         message_uuid_raw.data[i] = header[3 + i];
0134     }
0135 #else
0136     message_uuid_raw = qFromBigEndian<quint128>(&header.data()[3]);
0137 #endif
0138     QBluetoothUuid message_uuid = QBluetoothUuid(message_uuid_raw);
0139 
0140     // Check if we have the full message including its data
0141     QByteArray data = mSocket->read(message_length);
0142     if (data.size() != message_length) {
0143         mSocket->rollbackTransaction();
0144         return false;
0145     }
0146 
0147     Q_ASSERT(receivedProtocolVersion || message_type == MESSAGE_PROTOCOL_VERSION);
0148 
0149     // Parse the different message types
0150     if (message_type == MESSAGE_OPEN_CHANNEL) {
0151         // The other endpoint requested us to open a channel
0152         Q_ASSERT(message_length == 0);
0153 
0154         addChannel(message_uuid);
0155     } else if (message_type == MESSAGE_READ) {
0156         // The other endpoint has read some data and requests more data
0157         Q_ASSERT(message_length == 2);
0158         // Read the number of bytes requested (2 bytes, Big-Endian)
0159         uint16_t additional_read = qFromBigEndian<uint16_t>(data.data());
0160         Q_ASSERT(additional_read > 0);
0161 
0162         // Check if we haven't closed the channel in the meanwhile
0163         //    (note: different from the user's endpoint of a closed channel, since we might have outstanding buffers)
0164         auto iter = channels.find(message_uuid);
0165         if (iter != channels.end() && (*iter)->connected) {
0166             auto channel = *iter;
0167 
0168             // We have "additional_read" more bytes we can safely write in this channel
0169             channel->freeWriteAmount += additional_read;
0170             mSocket->commitTransaction();
0171             // We might still have some data in the write buffer
0172             Q_EMIT channel->writeAvailable();
0173             return true;
0174         }
0175     } else if (message_type == MESSAGE_WRITE) {
0176         // The other endpoint has written data into a channel (because we requested it)
0177         Q_ASSERT(message_length > 0);
0178 
0179         // Check if we haven't closed the channel in the meanwhile
0180         //    (note: different from the user's endpoint of a closed channel, since we might have outstanding buffers)
0181         auto iter = channels.find(message_uuid);
0182         if (iter != channels.end() && (*iter)->connected) {
0183             auto channel = *iter;
0184 
0185             Q_ASSERT(channel->requestedReadAmount >= message_length);
0186 
0187             // We received some data, so update the buffer and the amount of outstanding read requests
0188             channel->requestedReadAmount -= message_length;
0189             channel->read_buffer.append(std::move(data));
0190 
0191             mSocket->commitTransaction();
0192             // Indicate that the channel can read some bytes
0193             Q_EMIT channel->readyRead();
0194             return true;
0195         }
0196     } else if (message_type == MESSAGE_CLOSE_CHANNEL) {
0197         // The other endpoint wants to close a channel
0198         Q_ASSERT(message_length == 0);
0199 
0200         // Check if we haven't closed the channel in the meanwhile
0201         //    (note: different from the user's endpoint of a closed channel, since we might have outstanding buffers)
0202         auto iter = channels.find(message_uuid);
0203         if (iter != channels.end()) {
0204             auto channel = *iter;
0205 
0206             // We don't want signals anymore, since the channel is closed
0207             disconnect(channel.data(), nullptr, this, nullptr);
0208             removeChannel(message_uuid);
0209         }
0210     } else if (message_type == MESSAGE_PROTOCOL_VERSION) {
0211         // Checks for protocol compatibility
0212         Q_ASSERT(message_length >= 4);
0213         // Read the lowest & highest version supported (each 2 bytes, Big-Endian)
0214         uint16_t lowest_version = qFromBigEndian<uint16_t>(&data.data()[0]);
0215         uint16_t highest_version = qFromBigEndian<uint16_t>(&data.data()[2]);
0216 
0217         Q_ASSERT(lowest_version == 1);
0218         Q_ASSERT(highest_version >= 1);
0219         receivedProtocolVersion = true;
0220     } else {
0221         // Other message types are not supported
0222         Q_ASSERT(false);
0223     }
0224 
0225     mSocket->commitTransaction();
0226     return true;
0227 }
0228 
0229 QBluetoothUuid ConnectionMultiplexer::newChannel()
0230 {
0231     // Create a random uuid
0232     QBluetoothUuid new_id(QUuid::createUuid());
0233 
0234     // Open the channel on the other endpoint
0235     QByteArray message(3, (char)0);
0236     message[0] = MESSAGE_OPEN_CHANNEL;
0237     qToBigEndian<uint16_t>(0, &message.data()[1]);
0238 
0239 #if QT_VERSION_MAJOR == 5
0240     quint128 id_raw = new_id.toUInt128();
0241     message.append((const char *)id_raw.data, 16);
0242 #else
0243     const auto channelBytes = new_id.toByteArray();
0244     message.append(channelBytes.constData(), 16);
0245 #endif
0246     to_write_bytes.append(message);
0247 
0248     // Add the channel ourselves
0249     addChannel(new_id);
0250     // Write the data
0251     bytesWritten();
0252     return new_id;
0253 }
0254 
0255 void ConnectionMultiplexer::addChannel(QBluetoothUuid new_id)
0256 {
0257     MultiplexChannelState *channelState = new MultiplexChannelState();
0258     // Connect all channels queued, so that we have opportunities to combine read/write requests
0259 
0260     Q_ASSERT(unrequested_channels.size() <= 20);
0261 
0262     // Note that none of the channels knows its own uuid, so we have to add it ourselves
0263     connect(
0264         channelState,
0265         &MultiplexChannelState::readAvailable,
0266         this,
0267         [new_id, this]() {
0268             channelCanRead(new_id);
0269         },
0270         Qt::QueuedConnection);
0271     connect(
0272         channelState,
0273         &MultiplexChannelState::writeAvailable,
0274         this,
0275         [new_id, this]() {
0276             channelCanWrite(new_id);
0277         },
0278         Qt::QueuedConnection);
0279     connect(
0280         channelState,
0281         &MultiplexChannelState::requestClose,
0282         this,
0283         [new_id, this]() {
0284             closeChannel(new_id);
0285         },
0286         Qt::QueuedConnection);
0287 
0288     auto channelStatePtr = QSharedPointer<MultiplexChannelState>{channelState};
0289     channels[new_id] = channelStatePtr;
0290     unrequested_channels[new_id] = new MultiplexChannel{channelStatePtr};
0291     // Immediately ask for data in this channel
0292     Q_EMIT channelStatePtr->readAvailable();
0293 }
0294 
0295 std::unique_ptr<MultiplexChannel> ConnectionMultiplexer::getChannel(QBluetoothUuid channelId)
0296 {
0297     auto iter = unrequested_channels.find(channelId);
0298     if (iter == unrequested_channels.end()) {
0299         return nullptr;
0300     } else if (!(*iter)->isOpen()) {
0301         // Delete the channel
0302         delete *iter;
0303         unrequested_channels.erase(iter);
0304         // Don't return closed channels
0305         return nullptr;
0306     } else {
0307         auto channel = *iter;
0308         unrequested_channels.erase(iter);
0309         return std::unique_ptr<MultiplexChannel>{channel};
0310     }
0311 }
0312 
0313 std::unique_ptr<MultiplexChannel> ConnectionMultiplexer::getDefaultChannel()
0314 {
0315     return getChannel(QBluetoothUuid{QStringLiteral(DEFAULT_CHANNEL_UUID)});
0316 }
0317 
0318 void ConnectionMultiplexer::bytesWritten()
0319 {
0320     if (to_write_bytes.size() > 0) {
0321         // If we have stuff to write, try to write it
0322         auto num_written = mSocket->write(to_write_bytes);
0323         if (num_written <= 0) {
0324             // On error: disconnected will be called later
0325             // On buffer full: will be retried later
0326             return;
0327         } else if (num_written == to_write_bytes.size()) {
0328             to_write_bytes.clear();
0329         } else {
0330             to_write_bytes.remove(0, num_written);
0331             return;
0332         }
0333     }
0334 }
0335 
0336 void ConnectionMultiplexer::channelCanRead(QBluetoothUuid channelId)
0337 {
0338     auto iter = channels.find(channelId);
0339     if (iter == channels.end())
0340         return;
0341     auto channel = *iter;
0342 
0343     // Check if we can request more data to read without overflowing the buffer
0344     if (channel->read_buffer.size() + channel->requestedReadAmount < channel->BUFFER_SIZE) {
0345         // Request the exact amount to fill up the buffer
0346         auto read_amount = channel->BUFFER_SIZE - channel->requestedReadAmount - channel->read_buffer.size();
0347         channel->requestedReadAmount += read_amount;
0348 
0349         // Send a MESSAGE_READ request for more data
0350         QByteArray message(3, (char)0);
0351         message[0] = MESSAGE_READ;
0352         qToBigEndian<uint16_t>(2, &message.data()[1]);
0353 #if QT_VERSION_MAJOR == 5
0354         quint128 id_raw = channelId.toUInt128();
0355         message.append((const char *)id_raw.data, 16);
0356 #else
0357         const auto channelBytes = channelId.toByteArray();
0358         message.append(channelBytes.constData(), 16);
0359 #endif
0360         message.append(2, 0);
0361         qToBigEndian<int16_t>(read_amount, &message.data()[19]);
0362         to_write_bytes.append(message);
0363         // Try to send it immediately
0364         bytesWritten();
0365     }
0366 }
0367 
0368 void ConnectionMultiplexer::channelCanWrite(QBluetoothUuid channelId)
0369 {
0370     auto iter = channels.find(channelId);
0371     if (iter == channels.end())
0372         return;
0373     auto channel = *iter;
0374 
0375     // Check if we can freely send data and we actually have some data
0376     if (channel->write_buffer.size() > 0 && channel->freeWriteAmount > 0) {
0377         // Figure out how much we can send now
0378         auto amount = qMin((int)channel->write_buffer.size(), channel->freeWriteAmount);
0379         QByteArray data = channel->write_buffer.left(amount);
0380         channel->write_buffer.remove(0, amount);
0381         channel->freeWriteAmount -= amount;
0382 
0383         // Send the data
0384         QByteArray message(3, (char)0);
0385         message[0] = MESSAGE_WRITE;
0386         qToBigEndian<uint16_t>(amount, &message.data()[1]);
0387 
0388 #if QT_VERSION_MAJOR == 5
0389         quint128 id_raw = channelId.toUInt128();
0390         message.append((const char *)id_raw.data, 16);
0391 #else
0392         const auto channelBytes = channelId.toByteArray();
0393         message.append(channelBytes.constData(), 16);
0394 #endif
0395 
0396         message.append(data);
0397         to_write_bytes.append(message);
0398         // Try to send it immediately
0399         bytesWritten();
0400         // Let the channel's users know that some data has been written
0401         Q_EMIT channel->bytesWritten(amount);
0402 
0403         // If the user previously asked to close the channel and we finally managed to write the buffer, actually close it
0404         if (channel->write_buffer.isEmpty() && channel->close_after_write) {
0405             closeChannel(channelId);
0406         }
0407     }
0408 }
0409 
0410 void ConnectionMultiplexer::closeChannel(QBluetoothUuid channelId)
0411 {
0412     auto iter = channels.find(channelId);
0413     if (iter == channels.end())
0414         return;
0415     auto channel = *iter;
0416 
0417     // If the user wants to close a channel, then the user won't be reading from it anymore
0418     channel->read_buffer.clear();
0419     channel->close_after_write = true;
0420 
0421     // If there's still stuff to write, don't close it just yet
0422     if (!channel->write_buffer.isEmpty())
0423         return;
0424     channels.erase(iter);
0425     channel->connected = false;
0426 
0427     // Send the actual close channel message
0428     QByteArray message(3, (char)0);
0429     message[0] = MESSAGE_CLOSE_CHANNEL;
0430     qToBigEndian<uint16_t>(0, &message.data()[1]);
0431 
0432 #if QT_VERSION_MAJOR == 5
0433     quint128 id_raw = channelId.toUInt128();
0434     message.append((const char *)id_raw.data, 16);
0435 #else
0436     const auto channelBytes = channelId.toByteArray();
0437     message.append(channelBytes.constData(), 16);
0438 #endif
0439     to_write_bytes.append(message);
0440     // Try to send it immediately
0441     bytesWritten();
0442 }
0443 
0444 void ConnectionMultiplexer::removeChannel(QBluetoothUuid channelId)
0445 {
0446     auto iter = channels.find(channelId);
0447     if (iter == channels.end())
0448         return;
0449     auto channel = *iter;
0450 
0451     // Remove the channel from the channel list
0452     channels.erase(iter);
0453     channel->connected = false;
0454 
0455     Q_EMIT channel->disconnected();
0456 }
0457 
0458 #include "moc_connectionmultiplexer.cpp"