ADB协议解析说明

1概述:

Android调试桥(ADB)用于:跟踪所有连接到或运行在给定主机开发机器上的Android设备和模拟器实例-实现各种控制命令(例如:“adb shell”,“adb pull”等),以使客户端(命令行用户,或DDMS等帮助程序)受益。这些命令在ADB中被称为“服务”。作为一个整体,所有的工作通过以下组件:

1.1 ADB服务器这是在主机上运行的后台进程。它的目的是感知USB端口,以知道设备何时连接/删除,以及何时模拟器实例启动/停止。因此,它维护一个“连接设备”列表,并分配一个“状态”。离线,BOOTLOADER,恢复或在线(更多关于这下面)。ADB服务器实际上是一个巨大的多路复用循环,其目的是编排客户端、服务和设备之间的数据交换(实际上是数据包)。

1.2 ADB守护进程(adbd)“adbd”程序作为Android设备或仿真系统的后台进程运行。它的目的是连接到ADB服务器(通过USB为设备,通过TCP为模拟器)并提供一个在主机上运行的客户端服务很少。ADB服务器认为,当一个设备成功连接到它内部的adbd程序时,它是在线的。否则,设备是OFFLINE,这意味着ADB服务器检测到一个新的设备/模拟器,但无法连接到adbd守护进程。当设备处于引导加载程序或恢复模式时,引导加载程序和恢复状态对应于设备的备用状态。

1.3 ADB命令行客户端'adb'命令行程序用于从shell或脚本运行adb命令。它首先尝试在主机上定位ADB服务器,并将自动启动一个,如果没有找到。然后,客户端将其服务请求发送到ADB服务器。它不需要知道。目前,服务器和客户端都使用一个'adb'二进制文件。这使得分发和启动服务器更加容易。

1.4. 服务客户机可以与两种服务进行通信。主机服务:这些服务运行在ADB Server中,因此根本不需要与设备通信。一个典型的例子是“adb devices”,它用于返回当前已知的设备及其状态的列表。还有一些其他的服务。本地服务:这些服务要么在adbd守护进程中运行,要么由它在设备上启动。ADB服务器用于在客户端和运行在adbd上的业务之间复用流。在这种情况下,它的角色是发起连接,然后是数据的传递。

2协议细节:

2.1 Client <->服务器协议:这详细说明了ADB客户机和ADB服务器本身之间使用的协议。ADB服务器在TCP:localhost:5037上侦听。

客户端发送请求的格式如下:

1. 给出有效负载长度的4字节的十六进制字符串

2. 然后是有效载荷本身。

例如,要查询ADB服务器的内部版本号,客户端将执行以下操作:

1. 连接到tcp: localhost: 5037

2. 发送字符串“000Chost:version”到相应的socket

'host:'前缀用来表示请求是指向服务器本身的(我们稍后会讨论其他类型的请求)。内容长度是用ASCII编码的,以便于调试。

服务器应该用以下方式之一来响应请求:

1. 如果成功,则使用4字节的"OKAY"字符串

2. 对于失败,4字节的“FAIL”字符串,后面是4字节的十六进制长度,然后是给出失败原因的字符串。

3.作为一个特殊的例外,对于'host:version',

4字节与服务器内部版本号对应的十六进制字符串注意,在OKAY之后,连接仍然是活动的,这允许客户端发出其他请求。

但在某些情况下,OKAY甚至会改变连接的状态。

例如,'host:transport:<serialnumber>'请求的情况,其中'<serialnumber>'用于标识给定的设备/模拟器;在“OKAY”回答之后,客户端发出的所有进一步请求将直接转到相应的adbd守护进程。文件services . txt列出了ADB当前实现的所有服务。

2.2运输:ADB传输对ADB服务器与一个设备或模拟器之间的连接进行建模。目前有两种运输方式:—USB传输,物理设备通过USB传输—本地传输,对于运行在主机上的模拟器,通过TCP连接到服务器理论上,应该可以编写代理的本地传输ADB服务器与连接到另一台机器/运行在另一台机器上的设备/模拟器之间的连接。不过,这还没有做到。

每个传输可以在客户端之间携带一个或多个多路复用流以及它们指向的设备/模拟器。ADB服务器必须处理意外的传输断开(例如,当设备被物理拔出)正常。

2.3协议传输层处理“message”,它由一个24字节的报头和(可选的)负载组成。报头由6个32位字节组成,这些字以小端字节格式通过网络发送。

struct message {
    unsigned command;       /**命令标识符常量 **/
    unsigned arg0;          /**第一个参数 **/
    unsigned arg1;          /**第二个参数 **/
    unsigned data_length;   /**有效载荷长度(允许为0) **/
    unsigned data_crc32;    /**crc32的数据有效载荷 **/
    unsigned magic;         /**command ^ 0xffffffff **/
};

收到一个无效的消息头,或者错误的payload,或者无法识别的消息指令,将会导致关闭和对端的连接;这个协议取决于共享状态,消息流中的任何中断都将导致状态不同步。

以下章节描述了六种定义的消息类型。他们的格式是COMMAND(arg0,arg1,payload),payload 一般是一段字符串,或者如果没有内容需要发送则是空字符串。

标识符“local-id”和”remote-id”是对于发送者的描述,而如果作为一个接收者,则是反过来。

--- CONNECT(version, maxdata, "system-identity-string") ----------------

CONNECT消息建立了远程连接的存在。该版本用于确保协议兼容性,maxdata声明远程连接能够接收的最大消息体大小。目前,version=Ox01000000和maxdata=4096当双方之间建立连接时,会发送一条CONNECT消息。在接收到CONNECT消息之前,不能发送其他消息。在CONNECT消息之前收到的任何消息都必须被忽略。如果接收到的CONNECT消息版本未知或maxdata值不匹配,则必须关闭与对端的连接。系统标识字符串应该是"<systemtype>:<serialno>:<banner>",其中systemtype是"bootloader", "device",或"host", serialno一般是唯一ID或空值,banner是人类可识别的版本或标识符字符串,一般是有意义的属性。

--- AUTH(type, 0, "data") ----------------------------------------------

AUTH消息通知接收方,连接到发送方需要身份验证。如果type为TOKEN(1),则数据是一个随机的令牌,接收者可以用私钥签名。接收方回复一个AUTH报文,其中type为SIGNATURE(2), data为签名。如果签名验证通过,发送方回复一个CONNECT报文。如果签名验证失败,发送方回复一个新的AUTH包和一个新的随机令牌,以便接收方可以重试使用不同的私钥进行签名。一旦接收方尝试了所有的私钥,它就可以回复一个AUTH包,其中type是RSAPUBLICKEY(3), data是公钥。如果可能的话,可能会显示一个屏幕上的确认,让用户确认他们想要在设备上安装公钥。

--- OPEN(local-id, 0, "destination") -----------------------------------

OPEN消息标志着发送端有一个数据流需要连接到接收端,这个数据流是使用的local-id为标识,需要连接到的接收端的destination位于消息的payload里。local-id不为0.
OPEN消息之后,将会收到一个标志着连接建立成功的READY消息,或者一个连接建立失败的CLOSE消息。有时READY消息会同时接收到。

* "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, "") -------------------------------------

READY消息标志着发送端的数据流已经准备OK,可以开始发送数据了。此时的消息里的local-id 和接收端的remote-id是对应的。
Local-id 和remote-id都不可能为0。
READY消息里包含的remote-id如果和接收端没有对应,这个数据流需要丢弃。
除了第一个READY消息之外,所有的local-id 都应该被忽略不处理,这个主要是用来建立连接的。如果后续还会往同一个数据流上发READY消息,此时local-id不能变化。

--- WRITE(0, remote-id, "data") ----------------------------------------

WRTE消息是发送端发给接收端的数据。以remote-id为标识。在payload里的数据长度一定要<=maxdata length。
一个WRITE消息包含了一个remote-id,如果这个remote-id在接收端没有对应,那么应该被丢弃。这个数据可能在发送的过程中,就已经被关闭了。此时其实要注意残留在usb里的数据,是否要清空。
一个WRITE消息的发送一定是在接收到READY之后。一旦一个WRITE消息发送了,直到收到了另一个READY消息之后,才会发送下一个WRITE消息。接收方也是一样,如果不按照这个接收,那么将会关闭这个连接。

--- CLOSE(local-id, remote-id, "") -------------------------------------

CLOSE消息标志着接收端和发送端的连接已经断开,接收端是remote-id,发送端是local-id。如果是OPEN失败时,此时会受到CLOSE,此时的CLOSE的local-id可能会为0,但是remote-id一定不能为0.
如果收到的CLOSE消息里包含的remote-id和接收端的不匹配,那么要被丢弃掉。此时数据流可能在发送的过程中,被接收端关闭掉。
接收端任何时候都不能对CLOSE消息进行回复。接收端虽没有强制要求,但还是应该主动取消发送数据。因为发送的数据会被忽略。

--- SYNC(online, sequence, "") -----------------------------------------

SYNC消息被用来保证当链接已经被断开后,在io里的数据会被丢弃掉。

当对端的连接offline时,此时应该发送SYNC(0,0),开始丢弃所有的消息。
当和对端的连接建立后,此时应该发送一个SYNC(1,TOKEN),然后继续丢弃消息。
当接收到SYNC(1,TOKEN)后,会再次开始接收对端的消息。

--- message command constants ------------------------------------------

#define A_SYNC 0x434e5953   //该命令用于adb push/pull命令
#define A_CNXN 0x4e584e43   //该命令用于pc和device进行adb连接时使用,该命令带有数据阶段
#define A_OPEN 0x4e45504f   //用于在devices端开启一个服务,譬如adb shell,adb logcat,adb remount等shell命令
#define A_OKAY 0x59414b4f   //表示接收ok,该命令没有数据阶段
#define A_CLSE 0x45534c43   //关闭对应的连接,关闭和打开都是双向的。
#define A_WRTE 0x45545257   //该命令用来向pc端或是device端发送数据,该命令一般都有数据阶段
#define A_AUTH 0x48545541

--- implementation details ---------------------------------------------

桥接程序的核心将使用三个线程。其中一个线程将是一个select/epoll循环,用于处理各种入栈和出栈连接以及到远程端的连接之间的io。远程端连接将实现为两个线程(一个用于读取,一个用于写入)和一个数据报套接字对,以提供主select/epoll线程和远程连接线程对之间的通道。这样做的原因是,对于usb连接Linux和osx上的内核接口不允许你做有意义的事情nonblocking io。消息头的端口号交换将(根据需要)在远程连接线程对中发生,程序的其余部分将始终将消息头值视为本机端口号。桥梁程序将能够有许多迷你服务器编译。它们将以已知的名称发布(例如“shell”,“fs-bridge”等),并且在接收到OPENO到这样的服务时,桥接程序将创建一个流套接字对,并生成一个线程或子进程来处理io。

--- simplified / embedded implementation -------------------------------

对于有限的环境,如引导加载程序,允许使用预先分配的通道ID号支持较少的固定数量的通道,以便在任何给定的时间只有一个流可以连接到引导加载程序端点。协议保持不变,但“嵌入式”版本的动态性降低了。引导加载程序将支持两个流。一个“bootloader:debug”流,可以打开以从引导加载程序获取调试信息;一个“bootloader:control”流,它将支持一组基本的引导加载程序命令。

Example command stream dialogues:
"flash_kernel,2515049,........\n" "okay\n"
"flash_ramdisk,5038,........\n" "fail,flash write error\n"
"bogus_command......" <CLOSE>

相关文章

学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习...
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面...
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生...
Can’t connect to local MySQL server through socket \'/v...
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 ...
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服...