You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
287 lines
12 KiB
287 lines
12 KiB
|
|
--- a replacement for aproto ------------------------------------------- |
|
|
|
When it comes down to it, aproto's primary purpose is to forward |
|
various streams between the host computer and client device (in either |
|
direction). |
|
|
|
This replacement further simplifies the concept, reducing the protocol |
|
to an extremely straightforward model optimized to accomplish the |
|
forwarding of these streams and removing additional state or |
|
complexity. |
|
|
|
The host side becomes a simple comms bridge with no "UI", which will |
|
be used by either commandline or interactive tools to communicate with |
|
a device or emulator that is connected to the bridge. |
|
|
|
The protocol is designed to be straightforward and well-defined enough |
|
that if it needs to be reimplemented in another environment (Java |
|
perhaps), there should not problems ensuring perfect interoperability. |
|
|
|
The protocol discards the layering aproto has and should allow the |
|
implementation to be much more robust. |
|
|
|
|
|
--- protocol overview and basics --------------------------------------- |
|
|
|
The transport layer deals in "messages", which consist of a 24 byte |
|
header followed (optionally) by a payload. The header consists of 6 |
|
32 bit words which are sent across the wire in little endian format. |
|
|
|
struct message { |
|
unsigned command; /* command identifier constant (A_CNXN, ...) */ |
|
unsigned arg0; /* first argument */ |
|
unsigned arg1; /* second argument */ |
|
unsigned data_length; /* length of payload (0 is allowed) */ |
|
unsigned data_crc32; /* crc32 of data payload */ |
|
unsigned magic; /* command ^ 0xffffffff */ |
|
}; |
|
|
|
Receipt of an invalid message header, corrupt message payload, or an |
|
unrecognized command MUST result in the closing of the remote |
|
connection. The protocol depends on shared state and any break in the |
|
message stream will result in state getting out of sync. |
|
|
|
The following sections describe the six defined message types in |
|
detail. Their format is COMMAND(arg0, arg1, payload) where the payload |
|
is represented by a quoted string or an empty string if none should be |
|
sent. |
|
|
|
The identifiers "local-id" and "remote-id" are always relative to the |
|
*sender* of the message, so for a receiver, the meanings are effectively |
|
reversed. |
|
|
|
|
|
|
|
--- CONNECT(version, maxdata, "system-identity-string") ---------------- |
|
|
|
Command constant: A_CNXN |
|
|
|
The CONNECT message establishes the presence of a remote system. |
|
The version is used to ensure protocol compatibility and maxdata |
|
declares the maximum message body size that the remote system |
|
is willing to accept. |
|
|
|
Currently, version=0x01000000 and maxdata=256*1024. Older versions of adb |
|
hard-coded maxdata=4096, so CONNECT and AUTH packets sent to a device must not |
|
be larger than that because they're sent before the CONNECT from the device |
|
that tells the adb server what maxdata the device can support. |
|
|
|
Both sides send a CONNECT message when the connection between them is |
|
established. Until a CONNECT message is received no other messages may |
|
be sent. Any messages received before a CONNECT message MUST be ignored. |
|
|
|
If a CONNECT message is received with an unknown version or insufficiently |
|
large maxdata value, the connection with the other side must be closed. |
|
|
|
The system identity string should be "<systemtype>:<serialno>:<banner>" |
|
where systemtype is "bootloader", "device", or "host", serialno is some |
|
kind of unique ID (or empty), and banner is a human-readable version |
|
or identifier string. The banner is used to transmit useful properties. |
|
|
|
|
|
--- AUTH(type, 0, "data") ---------------------------------------------- |
|
|
|
Command constant: A_AUTH |
|
|
|
The AUTH message informs the recipient that authentication is required to |
|
connect to the sender. If type is TOKEN(1), data is a random token that |
|
the recipient can sign with a private key. The recipient replies with an |
|
AUTH packet where type is SIGNATURE(2) and data is the signature. If the |
|
signature verification succeeds, the sender replies with a CONNECT packet. |
|
|
|
If the signature verification fails, the sender replies with a new AUTH |
|
packet and a new random token, so that the recipient can retry signing |
|
with a different private key. |
|
|
|
Once the recipient has tried all its private keys, it can reply with an |
|
AUTH packet where type is RSAPUBLICKEY(3) and data is the public key. If |
|
possible, an on-screen confirmation may be displayed for the user to |
|
confirm they want to install the public key on the device. |
|
|
|
|
|
--- OPEN(local-id, 0, "destination") ----------------------------------- |
|
|
|
Command constant: A_OPEN |
|
|
|
The OPEN message informs the recipient that the sender has a stream |
|
identified by local-id that it wishes to connect to the named |
|
destination in the message payload. The local-id may not be zero. |
|
|
|
The OPEN message MUST result in either a READY message indicating that |
|
the connection has been established (and identifying the other end) or |
|
a CLOSE message, indicating failure. An OPEN message also implies |
|
a READY message sent at the same time. |
|
|
|
Common destination naming conventions include: |
|
|
|
* "tcp:<host>:<port>" - host may be omitted to indicate localhost |
|
* "udp:<host>:<port>" - host may be omitted to indicate localhost |
|
* "local-dgram:<identifier>" |
|
* "local-stream:<identifier>" |
|
* "shell" - local shell service |
|
* "upload" - service for pushing files across (like aproto's /sync) |
|
* "fs-bridge" - FUSE protocol filesystem bridge |
|
|
|
|
|
--- READY(local-id, remote-id, "") ------------------------------------- |
|
|
|
Command constant: A_OKAY |
|
|
|
The READY message informs the recipient that the sender's stream |
|
identified by local-id is ready for write messages and that it is |
|
connected to the recipient's stream identified by remote-id. |
|
|
|
Neither the local-id nor the remote-id may be zero. |
|
|
|
A READY message containing a remote-id which does not map to an open |
|
stream on the recipient's side is ignored. The stream may have been |
|
closed while this message was in-flight. |
|
|
|
The local-id is ignored on all but the first READY message (where it |
|
is used to establish the connection). Nonetheless, the local-id MUST |
|
not change on later READY messages sent to the same stream. |
|
|
|
|
|
--- WRITE(local-id, remote-id, "data") --------------------------------- |
|
|
|
Command constant: A_WRTE |
|
|
|
The WRITE message sends data to the recipient's stream identified by |
|
remote-id. The payload MUST be <= maxdata in length. |
|
|
|
A WRITE message containing a remote-id which does not map to an open |
|
stream on the recipient's side is ignored. The stream may have been |
|
closed while this message was in-flight. |
|
|
|
A WRITE message may not be sent until a READY message is received. |
|
Once a WRITE message is sent, an additional WRITE message may not be |
|
sent until another READY message has been received. Recipients of |
|
a WRITE message that is in violation of this requirement will CLOSE |
|
the connection. |
|
|
|
|
|
--- CLOSE(local-id, remote-id, "") ------------------------------------- |
|
|
|
Command constant: A_CLSE |
|
|
|
The CLOSE message informs recipient that the connection between the |
|
sender's stream (local-id) and the recipient's stream (remote-id) is |
|
broken. The remote-id MUST not be zero, but the local-id MAY be zero |
|
if this CLOSE indicates a failed OPEN. |
|
|
|
A CLOSE message containing a remote-id which does not map to an open |
|
stream on the recipient's side is ignored. The stream may have |
|
already been closed by the recipient while this message was in-flight. |
|
|
|
The recipient should not respond to a CLOSE message in any way. The |
|
recipient should cancel pending WRITEs or CLOSEs, but this is not a |
|
requirement, since they will be ignored. |
|
|
|
|
|
--- SYNC(online, sequence, "") ----------------------------------------- |
|
|
|
Command constant: A_SYNC |
|
|
|
The SYNC message is used by the io pump to make sure that stale |
|
outbound messages are discarded when the connection to the remote side |
|
is broken. It is only used internally to the bridge and never valid |
|
to send across the wire. |
|
|
|
* when the connection to the remote side goes offline, the io pump |
|
sends a SYNC(0, 0) and starts discarding all messages |
|
* when the connection to the remote side is established, the io pump |
|
sends a SYNC(1, token) and continues to discard messages |
|
* when the io pump receives a matching SYNC(1, token), it once again |
|
starts accepting messages to forward to the remote side |
|
|
|
|
|
--- message command constants ------------------------------------------ |
|
|
|
#define A_SYNC 0x434e5953 |
|
#define A_CNXN 0x4e584e43 |
|
#define A_AUTH 0x48545541 |
|
#define A_OPEN 0x4e45504f |
|
#define A_OKAY 0x59414b4f |
|
#define A_CLSE 0x45534c43 |
|
#define A_WRTE 0x45545257 |
|
|
|
|
|
|
|
--- implementation details --------------------------------------------- |
|
|
|
The core of the bridge program will use three threads. One thread |
|
will be a select/epoll loop to handle io between various inbound and |
|
outbound connections and the connection to the remote side. |
|
|
|
The remote side connection will be implemented as two threads (one for |
|
reading, one for writing) and a datagram socketpair to provide the |
|
channel between the main select/epoll thread and the remote connection |
|
threadpair. The reason for this is that for usb connections, the |
|
kernel interface on linux and osx does not allow you to do meaningful |
|
nonblocking IO. |
|
|
|
The endian swapping for the message headers will happen (as needed) in |
|
the remote connection threadpair and that the rest of the program will |
|
always treat message header values as native-endian. |
|
|
|
The bridge program will be able to have a number of mini-servers |
|
compiled in. They will be published under known names (examples |
|
"shell", "fs-bridge", etc) and upon receiving an OPEN() to such a |
|
service, the bridge program will create a stream socketpair and spawn |
|
a thread or subprocess to handle the io. |
|
|
|
|
|
--- simplified / embedded implementation ------------------------------- |
|
|
|
For limited environments, like the bootloader, it is allowable to |
|
support a smaller, fixed number of channels using pre-assigned channel |
|
ID numbers such that only one stream may be connected to a bootloader |
|
endpoint at any given time. The protocol remains unchanged, but the |
|
"embedded" version of it is less dynamic. |
|
|
|
The bootloader will support two streams. A "bootloader:debug" stream, |
|
which may be opened to get debug messages from the bootloader and a |
|
"bootloader:control", stream which will support the set of basic |
|
bootloader commands. |
|
|
|
Example command stream dialogues: |
|
"flash_kernel,2515049,........\n" "okay\n" |
|
"flash_ramdisk,5038,........\n" "fail,flash write error\n" |
|
"bogus_command......" <CLOSE> |
|
|
|
|
|
--- future expansion --------------------------------------------------- |
|
|
|
I plan on providing either a message or a special control stream so that |
|
the client device could ask the host computer to setup inbound socket |
|
translations on the fly on behalf of the client device. |
|
|
|
|
|
The initial design does handshaking to provide flow control, with a |
|
message flow that looks like: |
|
|
|
>OPEN <READY >WRITE <READY >WRITE <READY >WRITE <CLOSE |
|
|
|
The far side may choose to issue the READY message as soon as it receives |
|
a WRITE or it may defer the READY until the write to the local stream |
|
succeeds. A future version may want to do some level of windowing where |
|
multiple WRITEs may be sent without requiring individual READY acks. |
|
|
|
------------------------------------------------------------------------ |
|
|
|
--- smartsockets ------------------------------------------------------- |
|
|
|
Port 5037 is used for smart sockets which allow a client on the host |
|
side to request access to a service in the host adb daemon or in the |
|
remote (device) daemon. The service is requested by ascii name, |
|
preceeded by a 4 digit hex length. Upon successful connection an |
|
"OKAY" response is sent, otherwise a "FAIL" message is returned. Once |
|
connected the client is talking to that (remote or local) service. |
|
|
|
client: <hex4> <service-name> |
|
server: "OKAY" |
|
|
|
client: <hex4> <service-name> |
|
server: "FAIL" <hex4> <reason> |
|
|
|
|