Yaitza

adb介绍

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的启动日志:

bash
$ adb kill-server && adb devices

  • daemon not running. starting it now on port 5037 *
  • daemon started successfully *
    1
    2
    3
    4
    5
    6
    7

    通过以下命令,可以看到TCP的5037端口,在侦听连接:

    bash
    $ netstat -l | grep 5037
    Proto Recv-Q Send-Q Local Address Foreign Address State
    tcp 0 0 127.0.0.1:5037 0.0.0.0:* LISTEN

当我们执行一些常用的adb命令时,譬如adb devices,adb shell, server就自动启动了,也可以通过adb start-server来启动;如果想要停止server的运行,可以通过adb kill-server来杀掉server进程。

server只有一个,但client是可以有多个的。打开两个命令行,都输入adb shell命令,然后,再在第三个命令行输入以下命令,查看输出结果:

bash
$ lsof -i | grep adb
COMMAND PID TYPE DEVICE NODE NAME
adb 23613 IPv4 402225 TCP localhost:38491->localhost:5037 (ESTABLISHED)
adb 23710 IPv4 407399 TCP localhost:38494->localhost:5037 (ESTABLISHED)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

可以看到,建立了两个TCP连接,端口号的对应关系分别是:38491到5037, 38494到5037。这说明,多个clients可以同时和server建立连接。

## 2.2 adb的两个数据通道

### 2.2.1 client与server的数据通道

这个数据通道是一个本地TCP连接,adb server启动以后,在本地的5037端口侦听。adb client通过本地的随机端口与5037端口建立连接。

在这个通道上,client向server发送的命令都遵循如下格式:

1. 命令的长度(Length),由四位的十六进制表示
2. 实际的命令(Payload),通过ASCII编码

譬如,查看adb当前的版本,client会发起如下命令:

000Chost:version

000C表示"host:version"这条命令的长度为12个字节。命令中使用了host前缀,目的是为了区分其他类型的命令(后面还会看到shell前缀的命令),host前缀的命令可以理解为只在client与server这个数据通道上存在。

server收到client的请求后,返回的数据遵循如下格式:

1. 如果成功,则返回四个字节的字符串"OKAY"
2. 如果失败,则返回四个字节的字符串"FAIL"和出错原因
3. 如果异常,则返回错误码

当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的一个工作时序:

<div align="center"><img src="https://raw.githubusercontent.com/yaitza/yaitza.github.io/master/images/images//Tools/2-2017-05-03-Intro-adb.png" /></div>

***

# 3. 其他

## 3.1 源码目录

adb的源码在system/core/adb目录下,adb和adbd两个二进制程序都是从这个目录下的代码中编译出来的,通过宏编译开关**ADB_HOST**来控制:

``` c
#if ADB_HOST
/* code for adb */
#else
/* code for adbd */
#endif

3.2 USB Vendor ID

当通过USB线连接手机时,adb需要检查手机厂商的ID,即Vendor ID。
adb已经内置了很多Vendor ID,但仍然不能涵盖所有的手机厂商。

当出现adb无法找到设备时,我们需要手工添加Vendor ID,如何知道手机厂商的Vendor ID是多少呢? 通过lsusb命令可以查看:

bash
duanqizhi@xo:~$ lsusb
Bus 002 Device 005: ID 413c:2107 Dell Computer Corp.
Bus 002 Device 004: ID 046d:c077 Logitech, Inc.
Bus 002 Device 003: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 002 Device 002: ID 8087:8000 Intel Corp.
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 021: ID 2a45:0003
Bus 001 Device 002: ID 8087:8008 Intel Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub


无法识别设备的Vendor ID就是**2a45**,在**$HOME/.android/adb_usb.ini**文件末尾,将添加一行**2a45**即可。

## 3.3 几条有趣的adb命令

bash
$ adb hell
$ adb lolcat

本文引用至 http://duanqz.github.io/2015-05-21-Intro-adb.

🐶 您的支持将鼓励我继续创作 🐶