Summary: As mentioned on telegram, there are some fundamental issues with bluetooth. The only approach I could get working was multiplexing: manually sending multiple streams of data over a single connection. This is the description of that multiplexing protocol. I have an implementation of this protocol, but I'd first like some feedback to see if this is what we're going to use before finishing up those patches. See the document itself for more details. I make the protocol forward-compatible, although I don't expect we will need ever that. Test Plan: None, this is just a description. Reviewers: #kde_connect, andyholmes, albertvaka Reviewed By: #kde_connect, albertvaka Subscribers: albertvaka, kdeconnect Tags: #kde_connect Differential Revision: https://phabricator.kde.org/D17987
4.5 KiB
Bluetooth multiplexing protocol
For bluetooth in KDE Connect, we set up only a single connection between two KDE Connect clients (one as bluetooth server, one as client). This is in contrast to the LAN/TCP backend, where payloads are transferred over separate network connections.
Why do we do this?
Bluetooth has several issues, compared to TCP. Among these is that the amount of connections between two devices is limited, and that a lot of devices only support a single "server" socket. Also, not all devices can operate in a server mode, only the "master" bluetooth device can.
However, just simply putting the payload directly in the existing connection also has problems. For example, streams without a known size are impossible to support in this scheme. Also, we'd prefer KDE Connect to keep working while a large file is transferring.
What is the chosen solution?
To solve all this, we implemented a multiplexing protocol. This way, the bluetooth connection is divided up into any number of channels, which can send and receive data independently from each other.
The protocol is agnostic for server/client roles, requires no start-up communication and does not depend on bluetooth peculiarities (so could even by used in e.g. LAN).
Protocol description
Protocol version 1 Every channel on the connection is identified by a randomly chosen UUID. To communicate over the channel, or to communicate about a channel, messages are send. The following section explains the general message format, and the sections thereafter describe each type of message.
The first message sent must be the MESSAGE_PROTOCOL_VERSION
message. After that, other messages can be send in any order, but messages about unknown channels should be ignored.
The multiplexed connection starts with a single default/main channel with UUID a0d0aaf4-1072-4d81-aa35-902a954b1266
. This channel works like any other channel, but requires no communication to set up.
General message format
Every message send between the two endpoints has the following format.
| Message type | Message length | Channel UUID | Message data |
| 1 byte | 2 bytes (Big-Endian) | 16 bytes (Big-Endian) | "length" bytes |
| ------------------------- Header -------------------------- | |
Where the message type can be one of the following.
Message type | Value |
---|---|
MESSAGE_PROTOCOL_VERSION |
0 |
MESSAGE_OPEN_CHANNEL |
1 |
MESSAGE_CLOSE_CHANNEL |
2 |
MESSAGE_READ |
3 |
MESSAGE_WRITE |
4 |
MESSAGE_PROTOCOL_VERSION
This message should be the first message send, and never at a later time. Its format is as follows:
| MESSAGE_PROTOCOL_VERSION header | Lowest version supported | Highest version supported |
| 19 bytes (UUID ignored) | 2 bytes (Big-Endian) | 2 bytes (Big-Endian) |
This message should be the first message to send. Use the maximum version supported by both endpoints (if any), or otherwise close the connection.
Currently, no client will send this message with a version other than 1, but you must accept and check it, for forward compatibility.
MESSAGE_OPEN_CHANNEL
Before sending other messages about a channel, first send a MESSAGE_OPEN_CHANNEL
message, so the other endpoint knows the channel exists. A channel UUID should be chosen randomly and not reused.
This message always has length 0.
MESSAGE_CLOSE_CHANNEL
To close a channel, send a MESSAGE_CLOSE_CHANNEL
message. In your implementation, take care that your code gets to see the channel if it's quickly opened, little data is written, and then gets closed again.
This message always has length 0.
MESSAGE_READ
To prevent getting too much data on a channel unable to cope with that data, each channel needs to indicate that it wants to receive more data. To do so, send a MESSAGE_READ
message as follows:
| MESSAGE_READ header | Amount of additional data requested |
| 19 bytes | 2 bytes (Big-Endian) |
Note that the amount of data you request is in addition to the amounts you requested before.
MESSAGE_WRITE
To write data in a channel, you must first wait until the other endpoint indicates that it wants some data (with MESSAGE_READ
messages). Once you have this indication, you must not write more data than has been requested (writing less is allowed). The MESSAGE_WRITE
message has the following format:
| MESSAGE_WRITE header | Data to write in channel |
| 19 bytes | |