architecture.md 8.8 KB


sidebar_position: 1

Architecture

This part describes the architecture of native ADB and Web ADB, and why there are designed in this way.

Native ADB

Native ADB has three components:

``` ┌───────────────────────────────┐ ┌──────────────┐ │ │ │ │ │ ┌───────────┐ ┌────────┐ │ │ ┌──────────┐ │ │ │ ├─────► ├──┼──┼─► │ │ │ │ Client A │ │ │ │ │ │ Daemon A │ │ │ │ ◄─────┤ ◄──┼──┼─┤ │ │ │ └───────────┘ │ │ │ │ └──────────┘ │ │ │ │ │ │ │ │ ┌───────────┐ │ Server │ │ │ Device A │ │ │ ├─────► │ │ │ │ ┌──────────────┐ │ │ Client B │ │ │ │ └──────────────┘ │ │ │ │ ◄─────┤ │ │ │ ┌──────────┐ │ │ └───────────┘ │ ├──┼────────────────────┼─► │ │ │ │ │ │ │ │ Daemon B │ │ │ │ ◄──┼────────────────────┼─┤ │ │ │ Computer A └──▲──┬──┘ │ │ └──────────┘ │ │ │ │ │ │ │ └──────────────────────┼──┼─────┘ │ Device B │ │ │ │ │ ┌───────────────┐ │ │ └──────────────┘ │ │ │ │ │ ┌───────────┐ │ │ │ │ │ ├─┼──────┘ │ │ │ Client C │ │ │ │ │ ◄─┼─────────┘ │ └───────────┘ │ │ │ │ Computer B │ │ │ └───────────────┘ ```

Client

Client receives command line inputs, generates request packets, sends them to server over TCP.

Because there can be multiple devices connected to the computer, clients add a "host prefix" to specify the device.

Server

Server is in the same binary as client, but runs in a separate process with different command line arguments. Usually servers are spawned by clients when they cannot find one on localhost. To manually spawn a server, use adb server nodaemon. By default, it binds to localhost:5037.

It is also possible to use SSH tunnel to let clients connect to a server running on a remote machine.

Servers are responsible for discovering and connecting to devices. They also handle packets from clients.

Some packets should be processed by server itself (for example adb devices), it generates response packets and sends them to client.

Others need to be forwarded to daemons (for example adb shell). It finds the specified daemon using the "host prefix", rewrites the packet to remove "host prefix", and finally sends it to the daemon over USB (libusb/WinUSB) or TCP.

Because USB APIs only allow one connection to a device simultaneously, to use multiple CLI applications with one device, the server is required to multiplex the packets.

Daemon

Daemon runs on Android devices and emulators, it receives packets, handles them, and generates responses.

Historically, because most device only has one USB port, daemons can only handle one connection. But even after ADB over Wi-Fi has been added, one daemon can still handle one TCP connection.

Protocol

All packets between client-server and server-daemon are in the ADB packet format, but as mentioned before, client-server packets contain an extra "host prefix". ADB packet format will be described in packet chapter, while "host prefix" will be described in stream chapter.

Web ADB

Web ADB reuses native ADB daemons, but there is no client/server: One application, one connection, to one device.

``` ┌──────────────────────────────────┐ ┌──────────────┐ │ │ │ │ │ ┌──────────┐ ┌─────────────┐ │ │ ┌──────────┐ │ │ │ ├────► ├──┼──┼─► │ │ │ │ Core A │ │ Backend A │ │ │ │ Daemon A │ │ │ │ ◄────┤ ◄──┼──┼─┤ │ │ │ └──────────┘ └─────────────┘ │ │ └──────────┘ │ │ │ │ │ │ │ │ Device A │ │ │ │ │ ┌──────────────┐ │ │ └──────────────┘ │ │ │ ┌──────────┐ ┌─────────────┐ │ │ ┌──────────┐ │ │ │ ├────► ├──┼────────────────────┼─► │ │ │ │ Core B │ │ Backend B │ │ │ │ Daemon B │ │ │ │ ◄────┤ ◄──┼────────────────────┼─┤ │ │ │ └──────────┘ └─────────────┘ │ │ └──────────┘ │ │ │ │ │ │ JavaScript Runtime │ │ Device B │ │ │ │ │ └──────────────────────────────────┘ └──────────────┘ ```

Core

Core is the @yume-chan/adb package. It generates data in ADB protocol, without "host prefix" (not needed because packets are directly sent to daemons via a backend).

Backend

One backend defines one method to transmit and receive ADB packets. There are already multiple backend implementations, for example @yume-chan/adb-backend-usb and @yume-chan/adb-backend-ws.

One core instance requires one backend instance, so it only connects to one device.

Because JavaScript runtimes are generally more isolated, sharing devices between multiple application is not a consideration. However, it is still very easy to share a core instance within a single application, and if a runtime has more privileges, sharing core using a custom protocol is also not impossible.

Having Backend as an independent part also makes it extremely easy to port to other runtimes (Web Browsers, Node.js, Electron, etc.).

Possible Backend implementations:

  • Web Browsers
    • WebUSB API (USB)
    • WebSocket + Custom WebSockify forwarder (TCP)
  • Node.js/Electron
    • Any USB libraries (USB)
    • net module (TCP)
    • WebSocket + Custom WebSockify forwarder (TCP)

Comparison

Native ADB Web ADB
Who implements all ADB commands? Client Core
Who directly talks to device? Server Backend
How does them talk internally? TCP Socket (variation of ADB protocol) JavaScript API calls