adb通信分为两部分:adb client和adb server,以及adb server和adb daemon。
client和server之间的通信协议
adb server对本地的tcp 5037号端口进行监听,等待adb client的命令。client的每个命令都会包含两个部分,前一部分固定4个字节,以十六进制方式指定命令部分的长度。后一部分是真正的内容。发送命令的接口为writex,并最终调用_fh_socket_write,通过send发送出去。因此这两部分至少需要发送两个tcp包。
例如想要获取adb server的版本号,client首先连接本机的tcp 5037端口,然后发送“000C”和“host:version”。
server对client回复,分为如下情况
1、 成功,回复四字节串“OKAY”,后面跟的内容根据不同的命令而不同。
2、 失败,回复四字节串“FAIL”,然后跟四字节的十六进制长度,以及失败原因。
3、 对于host:version,回复4个字节的十六进制字串,代表server的内部版本号。
具体的命令如下。
命令 | 解释 |
host:version |
|
host:kill | 停止server |
host:devices |
|
host:track-devies |
|
host:emulator:<port> |
|
host:transport:<serial-number> | 连接指定serial-number的设备或者模拟器 |
host:transport-usb | 连接usb上的设备,如果usb上有不止一个设备,会失败。 |
host:transport-local | 通过tcp方式连接模拟器,如果有多个模拟器在运行,会失败。 |
host:transport-any | 连接usb设备或者模拟器都可以,但是如果有超过一个设备或模拟器,会失败。 |
host-serial:<serial-number>:<request> host-usb:<request> host-local:<request> | 向指定的设备发送特定的请求。同样如果存在多个设备的冲突,会失败。 |
host:<request> | 向当前连接的设备发送请求 |
<host-prefix>:get-serialno | 获取设备的serial-number |
<host-prefix>:get-state | 获取设备状态 |
<host-prefix>:forward:<local>;<remote> |
|
下面这些命令仅仅用于已经连接到某个设备,即在上面命令使用成功,连接到特定设备后,向特定设备发送命令,操作特定设备。
命令 | 解释 |
shell:command arg1 arg2 ... | 在设备上执行命令行操作 |
shell: | 参见commandline.c中的interactive_shell() |
remount: | 以读/写模式加载设备的文件系统 |
dev:<path> | 为client打开设备上的特定路径,用于读写问题。有可能由于权限问题而失败。 |
tcp:<port> | 尝试从设备连接本主机的某个tcp端口 |
tcp:<port>:<server-name> | 尝试从设备连接特定主机名的某个tcp端口 |
local:<path> | 尝试连接设备上的特定路径,路径是UNIX域名形式 |
localreserved:<path> localabstract:<path> localfilesystem:<path> | 尝试连接设备上的特定路径。 |
log:<name> | 打开设备上的特定日志文件,以便读取日志 |
framebuffer: | 尝试获取framebuffer的快照。即涉笔的屏幕快照 |
dns:<server-name> | 由serer执行来解析特定设备名 |
recover:<size> | 更新设备的恢复镜像 |
jdwp:<pid> | 连接特定VM进程上面的JDWP线程 |
track-jdwp |
|
sync: | 同步设备和主机上的文件 |
关于命令内容以及回复信息的更详细说明,参见adb源码文件夹下的SERVICES.TXT。
Transports
这个名词用于代表server和设备或者模拟器之间的通信协议。包含如下两种情况:
1、 USB transports。通过USB方式和物理设备通信。
2、 Local transports。通过本机的TCP连接方式和模拟器通信。
由此可以想到,其实可以通过TCP方式和其他机器上面的模拟器或者设备进行通信,但是这点还没有实现。
transport层用于处理消息,每个消息包含24个字节的头部,定义如下。
struct message {
unsigned command;
unsigned arg0;
unsigned arg1;
unsigned data_length;
unsigned data_crc32;
unsigned magic;
};
此部分具体内容参见protocol.txt。
参考文献:
1、 adb源码文件夹下OVERVIEW.TXT
2、 adb源码文件夹下SERVICES.TXT
3、 adb源码文件夹下protocol.txt
本文尝试列举客户端能够发送给ADB服务器的所有请求。关于adb客户端、adb服务器、adbd守护进程、adb服务的概念,以及这些组件如何相互配合完成ADB工作的细节,请参考之前发的文章《Android Debug Bridge 技术实现》。
==============================
主机服务
==============================
host:version
请求ADB服务器的内部版本号。作为一个特殊的例外,服务器将用4字节的十六进制字符串回应,返回服务器内部版本号,回应中没有“OKAY”和“FAIL”。
host:kill
请求ADB服务器立即退出。用于ADB客户端检测到在升级之后有废弃的ADB服务器仍在运行的情况。
host:devices
请求返回可用的Android设备及其状态的列表。在“OKAY”之后是4个字节的长度定义,然后是指定长度的表明当前设备状况的字符串,返回之后连接关闭。
host:track-devices
“host:devices”的一个变种,它不关闭连接;相反,每次添加或移除设备或者指定设备的状态发生变化,一个新的设备列表描述被发送。这就使得像DDMS这样的工具能够实时跟踪连接设备的状态,而不用重复轮训服务器。
host:emulator:<port>
这是一个特殊的请求,当启动一个新的模拟器时,该请求被发送到ADB服务器。<port>是一个十进制数字代表模拟器的ADB协议端口号,比如:模拟器将自动转发到adbd守护进程的TCP端口号。这个机制使得ADB服务器能够知道新的模拟器实例启动。
host:transport:<serial-number>
请求切换连接到<serial-number>指示的设备或模拟器。接到“OKAY”回应之后,所有的客户端请求将被直接发送给运行在指定设备上的adbd守护进程。(用来实现-s)
host:transport-usb
请求切换连接到通过USB连接到主机的设备上。如果存在多个这样的设备,请求将失败。(用来实现-d)
host:transport-local
请求切换连接到通过TCP连接的模拟器。如果有多个这样的模拟器实例在运行,请求将失败。(用来实现-e)
host:transport-any
另一个“host:transport”变种。请求切换连接到已连接的设备或正在运行的模拟器。如果可用的设备或模拟器多于一个,请求将失败。(用在-s、-d、-e都不被提供时)
host-serial:<serial-number>:<request>
这是一个特殊形式的请求,前缀“host-serial:<serial-number>:”表明客户端正在请求ADB服务器获得指定设备的信息。<request>可以是下述格式的一种。
host-usb:<request>
host-serial的一个变种,用于将连接到主机的唯一USB设备作为目标。如果没有这样的设备或有多个这样的设备,请求将失败。
host-local:<request>
host-serial的一个变种,用于将运行在主机上唯一的模拟器实例作为目标。如果没有这样的模拟器或有多个这样的模拟器,请求将失败。
host:<request>
当请求设备相关的信息时,“host:”也能被解释为“任何连接到主机的唯一设备或运行在主机上的唯一模拟器”。
<host-prefix>:get-product
暂无解释。
<host-prefix>:get-serialno
返回对应设备或模拟器的序列号。注意模拟器序列号是“emulator-5544”的形式。
<host-prefix>:get-state
返回指定设备的状态字符串。
<host-prefix>:forward:<local>;<remote>
请求ADB服务器将本地连接从<local>转移到指定设备上的<remote>地址。
这里的<host-prefix>可以是上面描述的host-serial、host-usb、host-local、host的任意一个,它表明目标是哪个设备或模拟器。
<local>的格式有以下几种:
tcp:<port> -> 在localhost:<port>上的TCP连接
local:<path> -> 在<path>上的Unix本地域套接字(Unix domain socket)
<remote>的格式有以下几种:
tcp:<port> -> 在设备上localhost:<port>的TCP连接
local:<path> -> 在设备上的Unix本地域套接字
jdwp:<pid> -> 在虚拟机进程<pid>中的JDWP线程
或者下面所描述的本地服务的任何一种。
==============================
本地服务
==============================
下面所有的请求都假设你已经切换传输到实际的设备,或者你使用上面所描述的请求前缀。
shell:command arg1 arg2 ...
在设备的shell中运行“command arg1 arg2 ...”,返回输出流及错误流。注意命令参数必须用空格分隔。如果一个参数包含空格,应该对它使用双引号。参数不能包含双引号和其他会导致错误的符号。
这是“adb shell”的非交互版本。
shell:
在设备上启动一个交互的shell会话。恰当的重定向标准输入、标准输出和标准错误输出。ADB服务器使用这个服务来实现“adb shell”,但是在输入被发送到设备之前,ADB服务器也会对输入做加工。(参考commandline.c中的interactive_shell()函数)
remount:
请求adbd守护进程重新挂载设备的文件系统到读/写模式下,而不是只读模式。在执行“adb sync”或者“adb push”之前,通常都需要这个服务。
在不允许该操作的特定的系统中,这个请求可能不成功。
dev:<path>
打开一个设备文件,直接将客户端连接到这个文件去执行读写。该服务对于调试除错很有用,但是需要特殊的权限,不能在所有的设备上运行。<path>是从文件系统根目录开始的全路径。
tcp:<port>
尝试连接到loclhost的tcp端口<port>上。
tcp:<port>:<server-name>
尝试从设备连接到<server-name>所指定机器的tcp端口<port>上。这个服务对调试只能在设备上显示的网络或代理问题很有用。
local:<path>
尝试连接到设备上的Unix域套接字<path>。
localreserved:<path>
localabstract:<path>
localfilesystem:<path>
几个local:<path>的变种,用来访问其他Android套接字命名空间。
log:<name>
打开一个系统日志(/dev/log/<name>),允许客户端直接读取。用来实现“adb logcat”。数据流对客户端是只读的。
framebuffer:
这个服务用来向客户端发送framebuffer的快照。它需要足够的权限,工作原理如下:
在“OKAY”之后,服务发送包含下列字段的16字节的二进制结构(低位优先格式):
depth: uint32_t: framebuffer深度
size: uint32_t: framebuffer大小(单位:字节)
width: uint32_t: framebuffer宽度(单位:像素)
height: uint32_t: framebuffer高度(单位:像素)
在当前的实现中,framebuffer深度总是16,大小总是:宽度*高度*2。
每当客户端想要一个快照时,它应该通过通道发送一个字节,触发服务将framebuffer数据按framebuffer大小指定的字节数发送给它。
如果adbd守护进程没有足够的权限打开framebuffer设备,那么连接会立即关闭。
dns:<server-name>
这个服务是个例外,因为它仅仅运行在ADB服务器中。它被用来实现USB联网,比如:通过主机为设备提供一个网络连接。
它用来在主机上执行gethostbyname(<address>),IP地址以4个字节的字符串返回。
recover:<size>
这个服务上传一个recovery影像到设备中。<size>必须与recovery影像文件大小一样。工作原理如下:
- 创建一个命名为/tmp/update的文件;
- 从客户端读取<size>大小的字节数,将它们写入到/tmp/update;
- 当影像文件成功读取之后,创建一个命名为/tmp/update.start的文件。
只有当设备处于recovery模式时,这个服务才能工作。此外,如果/tmp目录不存在,连接会立即关闭。
jdwp:<pid>
连接到运行在虚拟机进程<pid>中的JDWP线程。
track-jdwp
用于周期性的向客户端发送JDWP pids列表。返回数据格式如下:
<hex4>: 4个字符的十六进制字符串指定所有内容的长度
<content>: 一连串的格式为<pid> "/n"的ASCII行
DDMS使用这个服务知道设备或模拟器上正在运行哪些可以调试的进程。
注意没有仅获取一次列表的单步服务。
sync:
这个请求启动文件系统同步服务,用来实现“adb push”和“adb pull”。因为这个服务相当复杂,需要专门的文章来解释说明,如果有朋友感兴趣,我们以后专门讨论。