blob: d6efb64b7d939d987eac300c91ba6951eb82b44f [file] [log] [blame] [view]
# Understanding asockets
The data structure of asocket, with their queue, amessage, and apackets are described
in [internals.md](internals.md). But understanding asocket, how they are used, and how they are
paired is non-trivial. This document hopefully explains how bytes flow through them.
## Why ADB needs asocket
The concept of `asocket` was created to achieve two things.
- Carry multiple streams over a single pipe (originally that meant USB only).
- Manage congestion (propagate back-pressure).
With the introduction of TCP support, an abstraction layer (transport) was created
but TCP multiplexing was not leveraged. Even when using TCP, a transport still uses `asocket`
to multiplex streams.
## Data direction and asocket peers
- A asocket is uni-directional. It only allows data to be `enqueue`d.
- A asocket is paired with a peer asocket which handles traffic in the opposite direction.
## Types of asocket
There are several types of `asocket`. Some are easy to understand because they
extend `asocket`.
- JdwpSocket
- JdwpTracker
- SinkSocket
- SourceSocket
However there are "undeclared" types, whose behavior differs only via the `asocket`
function pointers.
- Local Socket (LS)
- Remote Socket (RS)
- Smart Socket (SS)
- Local Service Socket (LSS)
## Local socket (abbreviated LS)
A LS interfaces with a file descriptor to forward a stream of bytes
without altering it (as opposed to a LSS).
To perform its task, a LS leverages fdevent to request FDE_READ/FDE_WRITE notification.
```
LOCAL SOCKET TRANSPORT
┌────────────────────────────────────────────────┐ ┌──┐
┌──┐ write(3) │ ┌─────┐ enqueue() │ │
│ │◄─────────┼──┤Queue├─────────────◄──────────────◄──────────┼─────────(A_WRTE)◄──
│fd│ │ └─────┘ │ │ │
│ ├──────────►─────────────────┐ │ │ │
└──┘ read(3) └─────────────────┼──────────────────────────────┘ │ │
▼ └──┘
peer.enqueue()
```
A_WRTE apackets are forwarded directly to the LS by the transport. The transport
is able to route the apacket to the local asocket by using `apacket.msg1` which
points to the target local asocket `id`.
### Write to fd and Back-pressure
When a payload is enqueued, an LS tries to write as much as possible to its `fd`.
After the write attempt, the LS stores in its queue what could not be written.
Based on the volume of data in the queue, it sets `FDE_WRITE` and allows/forbids
more data to come.
- If there is data in the queue, the LS always requests `FDE_WRITE` events so it
can write the outstanding data.
- If there is less than `MAX_PAYLOAD` in the queue, LS calls ready on its peer (a RS),
so an A_OKAY apacket is sent (which trigger another A_WRTE packet to be send).
- If there is more than `MAX_PAYLOAD` in the queue, back-pressure is propagated by not
calling `peer->ready`. This will trigger the other side to not send more A_WRTE until
the volume of data in the queue has decreased.
### Read from fd and Back-pressure
When it is created, a LS requests FDE_READ from fdevent. When it triggers, it reads
as much as possible from the `fd` (within MAX_PAYLOAD to make sure transport will take it).
The data is then enqueueed on the peer.
If `peer.enqueue` indicates that the peer cannot take more updates, the LS deletes
the FDE_READ request.
It is re-installed when A_OKAY is received by transport.
## Remote socket (abbreviated RS)
A RS handles outbound traffic and interfaces with a transport. It is simple
compared to a LS since it merely translates function calls into transport packets.
- enqueue -> A_WRTE
- ready -> A_OKAY
- close -> A_CLSE on RS and peer.
- shutdown-> A_CLSE
A RS is often paired with a LS or a LSS.
```
LOCAL SOCKET (THIS) TRANSPORT
┌────────────────────────────────────────────────┐ ┌──┐
┌──┐ write(3) │ ┌─────┐ enqueue() │ │ │
│ │◄─────────┼──┤Queue├─────────────◄──────────────◄──────────┼─────────(A_WRTE)◄──
│fd│ │ └─────┘ │ │ │
│ ├──────────►─────────────────┐ │ ─ │ │
└──┘ read(3) └─────────────────┼──────────────────────────────┘ │ │
│ │ │
┌─────────────────▼─────────────────▲────────────┐ │ │
│ │ │ │ │
│ │ │ │ │
│ └─────────────────────►──────────────────(A_WRTE)───►
│ enqueue() │ │ │
└────────────────────────────────────────────────┘ └──┘
REMOTE SOCKET (PEER)
```
### RS creation
A RS is always created by the transport layer (on A_OKAY or A_OPEN) and paired with a LS or LSS.
- Upon A_OPEN: The transport creates a LSS to handle inbound traffic and peers it with
a RS to handle outbound traffic.
- Upon A_OKAY: When receiving this packet, the transport always checks if there is a
LS with the id matching `msg1`. If there is and it does not have a peer yet, a RS is
created, which completes a bi-directional chain.
## Local Service Socket (LSS)
A LSS is a wrapper around a `fd` (which is used to build a LS). The purpose is to process
inbound and outbound traffic when it needs modification. e.g.: The "framebuffer" service
involves invoking executable `screencap` and generating a header describing the payload
before forwarding the color payload. This could not be done with a "simple" LS.
The `fd` created by the LSS is often a pipe backed by a thread.
## Smart Socket (abbreviated SS)
These Smart sockets are only created on the host by adb server on accept(3) by the listener
service. They interface with a TCP socket.
Upon creation, a SS enqueue does not forward anything until the [smart protocol](services.md)
has provided a target device and a service to invoke.
When these two conditions are met, the SS selects a transport and A_OPEN the service
on the device. It gives the TCP socket fd to a LS and creates a RS to build a data flow
similar to what was described in the Local Socket section.
## Examples of dataflow
### Package Manager (Device service)
Let's take the example of the command `adb install -S <SIZE> -`. There are several install
strategies but for the sake of simplicity, let's focus on the one resulting in invoking
`pm install -S <SIZE> -` on the device and then streaming the content of the APK.
In the beginning there is only a listener service, waiting for `connect(3)` on the server.
```
ADB Client ADB Server TRANSPORT ADBd
┌──────────────────────┐ ┌─────────────────┐ │ ┌─────────────────┐
│ │ │ │ │ │ │
│ │ │ │ │ │ │
│ │ │ │ │ │ │
│ tcp * ───►* alistener │ │ │ │
│ │ │ │ │ │ │
│ │ │ │ │ │ │
└──────────────────────┘ └─────────────────┘ │ └─────────────────┘
┌──────────┐ ┌───────┐
│ APK │ │Console│
└──────────┘ └───────┘
```
Upon `accept(3)`, the listener service creates a SS and gives it the socket `fd`.
Then the client starts writing to the socket `|host:transport:XXXXXXX| |exec:pm pm install -S <SIZE> ->|`.
```
ADB Client ADB Server TRANSPORT ADBd
┌──────────────────────┐ ┌─────────────────┐ │ ┌─────────────────┐
│ │ │ │ │ │ │
│ │ │ │ │ │ │
│ │ │ │ │ │ │
│ tcp * ───►* SS │ │ │ │
│ │ │ │ │ │ │
│ │ │ │ │ │ │
└──────────────────────┘ └─────────────────┘ │ └─────────────────┘
┌──────────┐ ┌───────┐
│ APK │ │Console│
└──────────┘ └───────┘
```
The SS buffers the smart protocol requests until it has everything it needs from
the client.
The first part, `host:transport:XXXXXXX` lets the SS know which transport to use (it
contains the device identified `XXXXXXX`). The second part is the service to execute
`exec:pm pm install -S <SIZE> -`.
When it has both, the SS creates a LS to handle the TCP `fd`, and creates a RS to let
the LS talk to the transport. The last thing the SS does before replacing itself with a
LS (and giving it its socket fd) is sending an A_OPEN apacket.
```
ADB Client ADB Server TRANSPORT ADBd
┌──────────────────────┐ ┌─────────────────┐ │ ┌─────────────────┐
│ │ │ │ │ │ │
│ │ │ │ │ │ │
│ │ │ │ │ │ │
│ tcp * ◄───►* LS │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ └─────────► RS─┼──────┼─►───┼──────►A_OPEN │
└──────────────────────┘ └─────────────────┘ │ └─────────────────┘
┌──────────┐ ┌───────┐
│ APK │ │Console│
└──────────┘ └───────┘
```
So far only one side of the pipeline has been set up.
Upon reception of the A_OPEN on the device side, `pm` is invoked via `fork/exec`.
A socket pair end is given to a LS. A RS is also created to handle bytes generated by `pm`.
Now we have a full pipeline able to handle bidirectional streams.
```
ADB Client ADB Server TRANSPORT ADBd
┌──────────────────────┐ ┌─────────────────┐ │ ┌─────────────────┐
│ │ │ │ │ │ │
│ │ │ ┌──────────────┼──◄───┼─────┼─RS ◄───┐ │
│ │ │ ▼ │ │ │ │ │
│ ┌───────────►tcp * ◄───►* LS │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ └─────────► RS─┼──────┼─►───┼──────►LS │
└─────┼─────────────┼──┘ └─────────────────┘ │ └────────▲────────┘
│ │ │
┌─────┴────┐ ┌────▼──┐ ┌─────▼────┐
│ APK │ │Console│ │ PM │
└──────────┘ └───────┘ └──────────┘
```
At this point the client can `write(3)` the content of the apk to feed it to `pm`.
It is also able to `read(3)` to show the output of `pm` in the console.