Recovery模式如何支持ADB

这两天顺着recovery模式下一个网络需求, 为了便于调试又在recovery下做了ADB功能. 在Android早期的如4.4版本, recovery模式下支持ADB配置起来比较简单(支持adb devices/adb reboot/adb pull/push等常用指令), 但在Android 9.0下USB辅助设备一般都通过configfs的方式来配置了, 因此相对来说要适配的东西就多一些, 如果额外要适配adb shell命令, 则要修改adbd的源代码了.这篇文章就来看看如何在Recovery模式下解决这几个问题.

在进入正题之前, 先了解下USB相关的基础知识.

USB全称是Universal Serial Bus, 是一种广泛用于主机与外设之间的连接的串行总线.USB设备使用的是一种层级的结构, 最多可支持多达127个设备, 每个USB设备对应一个功能(function), 比如USB打印机提供了打印服务; 存储设备则提供了存储数据的功能.

USB system architecture

Android中的USB支持OTG(On The Go), 因此有两种模式, 一种是Android自身作为host,可以接入其他USB设备;一种是Android作为peripheral设备, Android可以连接到PC端, 比如要查看Android设备的存储内容时, 使用ADB时都必须将Android设置为peripheral模式.ADB是Android中用于开发调试的一个工具, 更详细的说明可以参考官网的说明Android Debuge Bridge.

Recovery下的ADB

适配的第一步是首先看看源码. 进入Recovery的代码/bootable/recovery下面有一个README.md的文档, 里边有大致说明了如何在recovery下使用ADB. 对USESRDEBUG/ENG版本, 默认是启动了adbd, 并且对于recovery模式通过adb devices看到的是一个如下设备:

1
2
3
4

$ adb devices
List of devices attached
1234567890abcdef recovery

还需要明确的一点是, 在recovery模式下, 只有部分adb指令可用, 比如adb root/adb push/pull, 如果要使用adb shell需要把/system分区挂载上来.看起来一切都比较简单了, 可通过adb reboot recovery之后却无法找到设备, 在PC端查看dmesg也没有任何USB设备枚举上来.

继续看文档, 上面说到, 如果设备使用了configfs这个配置文件系统的话, 需要设置相关的配置.那问题可能就出在这里: 使用了configfs来配置USB设备, 但是在recovery模式没有正常配置.

1
2
3
4
5
6

If device is using [configfs](https://www.kernel.org/doc/Documentation/usb/gadget_configfs.txt),
check if configfs has been properly set up in init rc scripts. See the [example
configuration](https://android.googlesource.com/device/google/wahoo/+/master/init.recovery.hardware.rc)
for Pixel 2 devices. Note that the flag set via sysfs (i.e. the one above) is no-op when using
configfs.

简单来说, 在Linux中, USB Gadget是一个具有UDC(USB Device Controller)的可以连接到一个USB Host的设备, 其通常具有串口通讯/数据存储的功能.而对于Host来说, 一个USB Gadget就是一个配置的集合而已, 每个配置包含很多接口, 也被称为功能(functions). 目前Linux已经包含了很多功能供USB Gadgets使用, 具体可以参看Linux的源码/kernel/drivers/usb/gadget.

那么具体来说ADB适配要经历哪几个步骤了? 接下来就来看一看recovery下适配ADB需要做的事情.

Recovery下的ADB适配

  • 挂载FunctionFs将USB用于ADB通信

在recovery代码目录/bootable/recovery/etc/init.rc中对ADB对应的FunctionFs做了配置:

1
2
3
4
5
6

on fs
write /sys/class/android_usb/android0/f_ffs/aliases adb
mkdir /dev/usb-ffs 0770 shell shell
mkdir /dev/usb-ffs/adb 0770 shell shell
mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000

这个有什么用了?看ADB的源码(/system/core/daemon), 大致可以看到, 只有挂载了functionfs, ADB才能基于USB的ep0端口进行通讯:

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


// /dev/usb-ffs/adb/ep0 (main.cpp)
if (access(USB_FFS_ADB_EP0, F_OK) == 0) {
// Listen on USB.
usb_init();
is_usb = true;
}

// usb.cpp
void usb_init() {
dummy_fd = adb_open("/dev/null", O_WRONLY);
CHECK_NE(dummy_fd, -1);
usb_ffs_init();
}

// usb.cpp
static void usb_ffs_init() {
D("[ usb_init - using FunctionFS ]");

usb_handle* h = new usb_handle();

if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
// Devices on older kernels (< 3.18) will not have aio support for ffs
// unless backported. Fall back on the non-aio functions instead.
h->write = usb_ffs_write;
h->read = usb_ffs_read;
} else {
h->write = usb_ffs_aio_write;
h->read = usb_ffs_aio_read;
aio_block_init(&h->read_aiob);
aio_block_init(&h->write_aiob);
}
h->kick = usb_ffs_kick;
h->close = usb_ffs_close;

D("[ usb_init - starting thread ]");
std::thread(usb_ffs_open_thread, h).detach();
}

需要了解USBFunctionFs的同学可以参考Linux的文档https://www.kernel.org/doc/Documentation/usb/functionfs.txt.

  • 创建ADB相关的功能配置

挂载configfs到某个目录, 并生成ADB相关的配置, 主要是USB的ProductID/VendorID以及设备序列号等信息

1
2
3
4
5
6
7
8
9
10
11
12
13

on init
mount configfs none /config
mkdir /config/usb_gadget/g1 0770 shell shell
write /config/usb_gadget/g1/idVendor <youre_usb_vendor_id>
write /config/usb_gadget/g1/idProduct <youre_usb_product_id>
mkdir /config/usb_gadget/g1/strings/0x409 0770
write /config/usb_gadget/g1/strings/0x409/serialnumber ${ro.serialno}
write /config/usb_gadget/g1/strings/0x409/manufacturer ${ro.product.manufacturer}
write /config/usb_gadget/g1/strings/0x409/product ${ro.product.model}
mkdir /config/usb_gadget/g1/functions/ffs.adb
write /config/usb_gadget/g1/os_desc/use 1
setprop sys.usb.configfs 1

配置完成后, /config/usb_gadget/g1目录下大致如下:

usb gadget configfs

  • 使能对应的USB Gadgets

在Android设备文件目录/sys/class/udc找到对应USB设备控制器的名称, 将其写入到对应的配置, 从而使得USB主机端可以正常枚举到该USB设备:

1
2
3
4
5
6
7
8

on property:sys.usb.ffs.ready=1
mkdir /config/usb_gadget/g1/configs/b.1 0777 shell shell
symlink /config/usb_gadget/g1/configs/b.1 /config/usb_gadget/g1/os_desc/b.1
mkdir /config/usb_gadget/g1/configs/b.1/strings/0x409 0770 shell shell
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "adb"
symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1
write /config/usb_gadget/g1/UDC "a800000.dwc3" # 这里写入对应的UDC名字

配置完成后, 重新打包下BOOT分区, 刷写后, 输入adb devices可以看到:

1
2
3

List of devices attached
1297270a recovery

说明修改起作用了, 但目前输入adb shell还是会提示/system/bin/sh目前找不到的错误, 原因是recovery并没有正常挂载system分区, 那么有没有可能不挂载system分区同时又能使用adb shell了? 看/bootable/recovery/etc/init.rc, recovery下实际有集成了一个命令工具集合busybox, 通过busybox我们应该也可以实现类似与正常模式下/system/bin/sh的功能, 这就需要修改Android中ADBD的源码了, 具体可以看下/system/core/adb/shell_serivce.cpp中的代码逻辑.

参考文献

0%