1. 概要
Android Debug Bridge(adb)
是一个Android的命令行工具,可以用来连接模拟器或实际的移动设备。做过Android开发的朋友一定对adb logcat, adb shell命令不陌生,Dalvik Debug Monitor Server(DDMS)后台也是运行的adb来实现监控调试移动设备。
总体而言,adb有两个用途:
-
监控连接设备 adb会监控所有已经连接设备(包括模拟器),譬如设备所处的状态:ONLINE,OFFLINE, BOOTLOADER或RECOVERY。
-
提供操作命令 adb提供了很多命令(adb shell, adb pull),来实现对设备的操控。这些操作命令在adb的体系里面,都称为“服务”。
2. 工作原理
adb的工作原理可以用下图来表示:
adb命令执行会涉及到三个组成部分(client, server,daemon)和两个数据通道,下面我们将深入对adb的工作原理进行介绍。
2.1 adb的三个组成元素
-
adb client 在本地客户端的可执行程序,下载Android SDK便可获取这个执行程序,通过在命令行执行adb,就启动了adb的本地客户端程序。我们常用的命令,adb devices, adb shell, adb logcat,都是先交由本地客户端程序处理的。
-
adb server 本地客户端并不能独立完成工作,当我们输入adb命令时,客户端会尝试连接本地的服务端程序。如果服务端程序没有启动,则启动一个本地的服务端程序。为什么客户端输入adb命令能够自动启动服务端呢?因为客户端和服务端实际上是集成在一个可执行程序里面的,在Linux系统上,是
adb
;在Windows系统上,是adb.exe
-
adb daemon(adbd) 在模拟器或移动设备上运行的后台服务。当Android系统起机的时候,由init程序启动adbd。如果adbd挂了,则adbd会由init重新启动。换言之,只要Android系统在运行,那
adbd
就是“不死的”,常年在伺服状态。
client和server虽然是同一个执行程序,但在命令行输入一条adb命令后,实际上完成了一次通信。在server启动的时候,会将自己绑定到本地的5037
端口,当client有请求到来时,便通过TCP连接server的5037端口。
通过以下命令,可以看到server的启动日志:
通过以下命令,可以看到TCP的5037端口,在侦听连接:
当我们执行一些常用的adb命令时,譬如adb devices,adb shell, server就自动启动了,也可以通过adb start-server来启动;如果想要停止server的运行,可以通过adb kill-server来杀掉server进程。
server只有一个,但client是可以有多个的。打开两个命令行,都输入adb shell命令,然后,再在第三个命令行输入以下命令,查看输出结果:
可以看到,建立了两个TCP连接,端口号的对应关系分别是:38491到5037, 38494到5037。这说明,多个clients可以同时和server建立连接。
2.2 adb的两个数据通道
2.2.1 client与server的数据通道
这个数据通道是一个本地TCP连接,adb server启动以后,在本地的5037端口侦听。adb client通过本地的随机端口与5037端口建立连接。
在这个通道上,client向server发送的命令都遵循如下格式:
- 命令的长度(Length),由四位的十六进制表示
- 实际的命令(Payload),通过ASCII编码
譬如,查看adb当前的版本,client会发起如下命令:
000Chost:version
000C表示”host:version”这条命令的长度为12个字节。命令中使用了host前缀,目的是为了区分其他类型的命令(后面还会看到shell前缀的命令),host前缀的命令可以理解为只在client与server这个数据通道上存在。
server收到client的请求后,返回的数据遵循如下格式:
- 如果成功,则返回四个字节的字符串”OKAY”
- 如果失败,则返回四个字节的字符串”FAIL”和出错原因
- 如果异常,则返回错误码
当Client收到Server返回的”OKAY”后,就可以发继续发起其他操作命令了。
2.2.2 server与adbd的数据通道
这个数据通道对client而言,完全是透明的,client不关注这个通道怎么建立以及怎么进行数据传输。
当连接模拟器时,这个数据通道也是一个本地的TCP连接;当连接实际的设备时,这个数据通道通常是USB数据线的连接,当前,adb也支持远程的TCP连接。
adb server启动后,会在5037端口侦听从client发起的TCP连接,同时,也会试图与5555~5585这些端口建立TCP连接。当Android模拟器启动或者手机连接上PC时,就会用到5555~5585这些端口,简单理解就是adbd在PC上占用的端口号。每一个adbd都会占用两个端口,一个偶数号端口,用于命令行的连接;一个奇数号端口,用于adb的连接。
client与adbd的数据传输是需要用到两个通道的,当与server建立第一个通道的连接后,需要向server发送transport命令,表示接下来,要与adbd进行数据传输。transport命令有以下一些形式:
- host:transport:<serial-number> 用于向指定的设备发送transport命令
- host:transport-usb 用于向USB连接的设备发送transport命令,当有多个设备通过USB连接时,server会返回“FAIL”
- host:transport-local 用于向模拟器发送transport命令,当有多个模拟器启动时,server会返回“FAIL”
- host:transport-any 用于向任何连接的设备或模拟器发送transport命令,有多个连接时,server会返回“FAIL”
当server返回“OKAY”后,client后续发送的数据,就直接传输到adbd了。譬如shell:command arg1 arg2 ...
命令,表示client往adbd发送的shell命令。
下图是在客户端输入adb shell ls
命令后,adb的一个工作时序:
3. 其他
3.1 源码目录
adb的源码在system/core/adb目录下,adb和adbd两个二进制程序都是从这个目录下的代码中编译出来的,通过宏编译开关ADB_HOST来控制:
3.2 USB Vendor ID
当通过USB线连接手机时,adb需要检查手机厂商的ID,即Vendor ID。 adb已经内置了很多Vendor ID,但仍然不能涵盖所有的手机厂商。
当出现adb无法找到设备时,我们需要手工添加Vendor ID,如何知道手机厂商的Vendor ID是多少呢? 通过lsusb
命令可以查看:
无法识别设备的Vendor ID就是2a45,在$HOME/.android/adb_usb.ini文件末尾,将添加一行2a45即可。