Welcome to Blogs @ Andrew Qu
Blog Index
All blogs
Search results

Android ADB Protocols

Summary

Describes Android ADB protocols with programming details.

Overview

Android ADB is a powerful development tool that is useful in all levels of Android development. It is very easy to use. Yet, the internal logics of ADB are very complex. Compounded with the lack of documentation, it is extremely difficult to understand how it works and how it is actually implemented.

This blog describes a small but key part of the ADB protocol - USB communications between host and device. This will aid the ad-hoc use of the ADB protocols. A sample use of this would be pushing contents from memory to the device without having to save to a disk file first.

ADB related source codes are located in "system/core/adb" folder. The available document files are (all in the adb folder): OVERVIEW.TXT
SERVICES.TXT
protocol.txt

SYNC.TXT (this file is mentioned, but apparently missing).

SYNC.TXT is supposed to describe the file transfer protocol (adb push, adb pull). Since it is missing, information described here has been the result of reverse enginering the source code. All protocols described in this Wiki has been verified and tested on Android ICS.

Overall ADB Architecture


Packet Structure

Regardless the connection type (USB or TCP/IP), the communication protocol between the host adb daemon and the device adbd daemon is the same. This is a packet based communication where data to be communicated are divided into packets. A packet has 2 parts: a 24 bytes header and "payload". The payload may be of zero length (this is the case for most control commands, such as reboot).

A packet is best described using a C++ class:

class AdbMessage
{
public:
    unsigned command;       /* command identifier constant, A_SYNC etc. */
    unsigned arg0;          /* first argument                   */
    unsigned arg1;          /* second argument                  */
    unsigned data_length;   /* length of payload (0 is allowed) */
    unsigned data_check;    /* checksum of data payload, crc32  */
    unsigned magic;         /* command ^ 0xffffffff             */
    unsigned char data[MAX_PAYLOAD];

public:
    AdbMessage();
    AdbMessage(unsigned int cmd);
    AdbMessage(unsigned int cmd, unsigned arg0, unsigned arg1, const char *data);
    AdbMessage(unsigned int cmd, unsigned arg0, unsigned arg1, void *data, unsigned data_len);
    ~AdbMessage();

    void SetCommand(unsigned int cmd);
    void SetRemoteID(unsigned remoteID);
    void SetData(void *sdata, int datlen);
};
There are 6 standard packet types, as the A_XXXX maros defined in below.
#define A_SYNC 0x434e5953
#define A_CNXN 0x4e584e43
#define A_OPEN 0x4e45504f
#define A_OKAY 0x59414b4f
#define A_CLSE 0x45534c43
#define A_WRTE 0x45545257
The type id, as an integer, are the equivalent of the ASCII characters, using little-endian. For example, A_SYNC = 'CNYS' = 0x434e5953. The required parameters for each packet type are described below using the AdbMessage() constructor.
#define A_VERSION 0x01000000
#define MAX_PAYLOAD 4096
#define MAX_DATA_SIZE 0x10000
OPEN

AdbMessage(A_OPEN, local_id, 0, action);
This is used to open a channel to communicate. It can aslo perform an action if it is given. Actions can be (not exhaustive):
"root:" - equivalent to "adb root"
"shell:shell command" - equivalent to "adb shell", for example, adb shell ll
"reboot:" - reboots the device
"reboot:bootloader" - reboots the device to the boot loader
"sync:" - Sync the channel for further communication.

channel_id - if you are doing ad-hoc programming, you can fix this number to any integer. If you are doing multi-channel communication, like the host side ADB daemon, you will then have to assign to each channel a nuique number. More details on this later.

If action="sync:" and the packet is sent to a device, then the device will respond with an OKAY packet (see below) that will contain the remote_id.

OKAY

AdbMessage(A_OKAY, local_id, remote_id, NULL);
This is used to respond to a packet if the operation is successful.
local_id is the id of the originator. remote_id is the id of the receiver. See examples later.

CLOSE

AdbMessage(A_CLSE, local_id, remote_id, NULL);
This is used to close a communication channel identified by the pair (local_id,remote_id).

WRITE

AdbMessage(A_WRTE, local_id, remote_id, data);
This is used to tranfer a piece of data from the originator to the receiver.

A_SYNC

NOTE: This is never used over the usb connection.
AdbMessage(A_SYNC, 1, 0, NULL); to synchronize a server/client session
AdbMessage(A_SYNC, 0, 0, NULL);  to abort a server/client session in case there are IO errors

Making a Connection

A connection must be made first before any communicaiton can happen between host and device.

Send →  AdbMessage(A_CNXN, A_VERSION, MAX_PAYLOAD, "host::");
Receive ←  AdbMessage(A_CNXN, A_VERSION, MAX_PAYLOAD, "device::");
Some ADB Commands adb root
Send → AdbMessage(A_OPEN, local_id, 0, "root:");
Receive ← AdbMessage(A_OKAY, remote_id, local_id, NULL);
case ok: 
  Receive ← AbdMessage(A_WRTE, remote_id, local_id, status);
  Send → AdbMessage(A_OKAY, local_id, remote_id, NULL);
  Send → AdbMessage(A_CLSE, local_id, remote_id, NULL);
case fail: 
  Receive ← AbdMessage(A_CLSE, remote_id, local_id, NULL);
  Send → AdbMessage(A_CLSE, local_id, remote_id, NULL);

status can be one of the following:
  adbd is already running as root
  starting adbd as root
  adbd cannot run as root in production builds

adb reboot
adb reboot-bootloader
Send → AdbMessage(A_OPEN, local_id, 0, "reboot:");
Receive ← AdbMessage(A_OKAY, remote_id, local_id, NULL);
Send → AdbMessage(A_OPEN, local_id, 0, "reboot:bootloader");
Receive ← AdbMessage(A_OKAY, remote_id, local_id, NULL);

There is no need to close the connection which is no longer valid after rebooting.

adb shell:command
Send → AdbMessage(A_OPEN, local_id, 0, "shell:command");
Receive ← AdbMessage(A_OKAY, remote_id, local_id, NULL);
Receive ← AdbMessage(A_WRTE, remote_id, local_id, data);
Send → AdbMessage(A_OKAY, local_id, remote_id, NULL);
...
Receive ← AdbMessage(A_WRTE, remote_id, local_id, data);
Send → AdbMessage(A_OKAY, local_id, remote_id, NULL);
Receive ← AdbMessage(A_CLSE, remote_id, local_id, NULL);
Send → AdbMessage(A_CLSE, local_id, remote_id, NULL);
Query File Attributes

This will be used when sending or receiving files. Assume A_OPEN has been sent successfully. The file attributes will be mode, size, and time, as shown in the following struct

typedef struct _rf_stat__ 
{
    unsigned id;    // ID_STAT('S' 'T' 'A' 'T')
    unsigned mode;
    unsigned size;
    unsigned time;
} FILE_STAT;
Send → AdbMessage(A_WRTE, local_id, remote_id, "STATnnnn");
Receive ← AdbMessage(A_OKAY, remote_id, local_id, NULL);
Send → AdbMessage(A_WRTE, local_id, remote_id, "remote file path");
Receive ← AdbMessage(A_OKAY, remote_id, local_id, NULL);
Receive ← AdbMessage(A_WRTE, remote_id, local_id, FILE_STAT);
Send → AdbMessage(A_OKAY, local_id, remote_id, NULL);
Where in STATnnnn, nnnn is the length of the file name. This can be formed using the following code sample:
   memcpy(buff, "STAT", 4);
  unsigned int len = strlen(file_name);
  memcpy(buff+4, &len, 4);
FILE_STAT in the receive packet will be a 16 bytes long buff. Pulling Files (adb pull)
Send → AdbMessage(A_OPEN, local_id, 0, "sync:");
Receive ← AdbMessage(A_OKAY, remote_id, local_id, NULL);
  Query File Attributes. If file exists, then proceed.
Send → AdbMessage(A_WRTE, local_id, remote_id, "RECVnnnn");
Receive ← AdbMessage(A_OKAY, remote_id, local_id, NULL);
Send → AdbMessage(A_WRTE, local_id, remote_id, "remote file name");
Receive ← AdbMessage(A_OKAY, remote_id, local_id, NULL);
Receive ← AdbMessage(A_WRTE, remote_id, local_id, "DATAnnnn......");
Send → AdbMessage(A_OKAY, local_id, remote_id, NULL);
Receive ← AdbMessage(A_WRTE, remote_id, local_id, data);
Send → AdbMessage(A_OKAY, local_id, remote_id, NULL);
   ...
Receive ← AdbMessage(A_WRTE, remote_id, local_id, "DONEnnnn");
Send → AdbMessage(A_WRTE, local_id, remote_id, "QUITnnnn");
Receive ← AdbMessage(A_CLSE, remote_id, local_id, NULL);
Send → AdbMessage(A_CLSE, local_id, remote_id, NULL);
Notes:
  • RECVnnnn - nnnn = length of the file name, see STATnnnn
  • DATAnnnn - nnnn = length of the DATA block. Max. 65k. There can be several DATAnnnn blocks if the file is larger than 65k
  • DONEnnnn, QUITnnnn - nnnn is binary 0
  • The DATA blocks may be in several different formats:
  • [DATAnnnn] [dddd...] [dddd...] [DONE0000]
  • [DATAnnnndddd...] [dddd...] [dddd...] [DONE0000]
  • [DATAnnnn] [dddd...] [dddd...] [dddd...DONE0000]
  • [DATAnnnn] [dddd...] [dddd...] [DATAnnnn...] ... [dddd...DONE0000]
  • [DATAnnnndddd...DONE0000]
where [...] reprents a packet. Pushing Files (adb push)
Send → AdbMessage(A_OPEN, local_id, 0, "sync:");
Receive ← AdbMessage(A_OKAY, remote_id, local_id, NULL);
   Query File Attributes. If file does not exist or can be overwritten, then proceed. 
Send → AdbMessage(A_WRTE, local_id, remote_id, "SENDnnnn");
Receive ← AdbMessage(A_OKAY, remote_id, local_id, NULL);
Send → AdbMessage(A_WRTE, local_id, remote_id, "remote file name");
Receive ← AdbMessage(A_OKAY, remote_id, local_id, NULL);
Send → AdbMessage(A_WRTE, local_id, remote_id, ",33206");
Receive ← AdbMessage(A_OKAY, remote_id, local_id, NULL);
Send → AdbMessage(A_WRTE, local_id, remote_id, "DATAnnnn");
Receive ← AdbMessage(A_OKAY, remote_id, local_id, NULL);
Send → AdbMessage(A_WRTE, local_id, remote_id, data_buf, buflen);
Receive ← AdbMessage(A_OKAY, remote_id, local_id, NULL);
  Repeat A_WRTE until nnnn bytes are sent 
  Repeat DATAnnnn until whole file contents have been transferred   
Send → AdbMessage(A_WRTE, local_id, remote_id, "DONEnnnn");
Receive ← AdbMessage(A_OKAY, remote_id, local_id, NULL);
Receive ← AdbMessage(A_WRTE, remote_id, local_id, "OKAYnnnn" or "FAILnnnn");
Send → AdbMessage(A_OKAY, local_id, remote_id, NULL);
Send → AdbMessage(A_WRTE, local_id, remote_id, "QUITnnnn");
Receive ← AdbMessage(A_CLSE, remote_id, local_id, NULL);
Send → AdbMessage(A_CLSE, local_id, remote_id, NULL);
Notes:
  • SENDnnnn - nnnn = length of the file name + file mode (",33206"). For example, if file name="log.txt", then length=13. nnnn is then 0D000000
  • File mode ",33206" corresponds to octal 100666 (-rw-rw-rw-). If you are creating a folder, this will be ",16895" or 40777oct (drwxrwxrwx)
  • DATAnnnn - nnnn = lenght of this DATA block. A data block cannot be larger than MAX_DATA_SIZE
  • DONEnnnn - nnnn sets the time of the file. You can either get the file time or use time() function.
  • OKAYnnnn, QUITnnnn - nnnn = 0
  • FAILnnnn - nnnn = 0. The packet will contain more failure information in the data member.
  • At every stage, you will have to add logic to handle error conditions.
Programming Notes

The magic member :

void AdbMessage::SetCommand(unsigned int cmd)
{
   this->command = cmd;
   this->magic = cmd ^ 0xffffffff;
}
The data_check member is the check sum of all data bytes to be transferred:
void AdbMessage::SetData(void *sdata, int datlen)
{
   if( datlen > MAX_PAYLOAD ) throw "Packet data too  long"
   memcpy(data, sdata, datlen);
   this->data_length = datlen;
   this->data_check = 0;
   unsigned char *x = (unsigned char *) sdata;
   while(datlen-- > 0) this->data_check += *x++;
}
Sending a packet
Always send the first 24 bytes of AdbMessage object first, followed by data[] if present. This can be done in a way similar to the following:
AdbWriteEndpointSync(adb_write_pipe, &msg, 24, &written, 5000);
AdbWriteEndpointSync(adb_write_pipe, msg.data, msg.data_length, &written, 2000);
Reading packets
Always read the first 24 bytes first. The check the data_length member. If it is not 0, then read data[]. For example,
AdbReadEndpointSync(adb_read_pipe, &msg, 24, &read, 4000);
if(msg.data_length > 0)
{
   AdbReadEndpointSync(adb_read_pipe, msg.data, msg.data_length, &read, 4000);
}
Ads from Google
Dr Li Anchor Profi
www.anchorprofi.de
Engineering anchorage plate design system
©Andrew Qu, 2015. All rights reserved. Code snippets may be used "AS IS" without any kind of warranty. DIY tips may be followed at your own risk.