4. Android USB系统框架概览:从App到内核的完整链路
Android USB系统,说白了就是一条从App到物理USB接口的管道。我刚开始接触Android USB时,总以为插上就能用,结果被各种权限、驱动、内核配置折磨得够呛。今天就带你看看这条管道到底分几层,每层又干些什么。
4.1 总体架构:四层结构
我个人习惯把Android USB框架分成四层来理解。你想想看,一个数据请求从抖音App发出来,要经过多少关卡才能送到USB口上?
| 层级 | 所在空间 | 核心组件 | 典型文件/模块 |
|---|---|---|---|
| 应用层 | 用户空间 | App / SDK API | android.hardware.usb.UsbManager |
| 框架层 | 用户空间 | USB Service / System Server | UsbService.java, UsbDeviceManager.java |
| 原生层 | 用户空间 | HAL / libusb / usbd | android.hardware.usb@1.0 |
| 内核层 | 内核空间 | USB Core / Gadget / HCD | drivers/usb/, gadget/ |
关键认识:Android USB主机侧(Host)和设备侧(Gadget)在内核层共用USB Core,但分支完全不同。主机侧走HCD(主机控制器驱动),设备侧走Gadget驱动栈。这两条路千万别搞混。
一个小技巧:我在项目里调试USB时,常常先用 lsusb 确认设备是否被内核识别,再用 dumpsys usb 看框架层状态。这两步能快速定位问题到底出在哪一层。
4.2 从上到下的数据流:以adb推送文件为例
假设你用 adb push 把一个文件从电脑推到手机里。数据是怎么走的?我们跟一遍。
- 发起端(电脑):adb客户端通过USB bulk端点发送数据。
- 手机端内核层:USB Gadget驱动收到数据,交给FunctionFS。
- 手机端原生层:
adbd守护进程从/dev/usb-ffs/adb/ep0读取数据。 - 手机端框架层:adbd调用
vold完成文件写入操作。
每一步都涉及权限检查、端点调度和缓冲区操作。我记得有一次在QCOM平台上调试,发现vold在处理大文件时反复超时,最后定位到是Gadget驱动的中断传输缓冲区太小。嗯,这里要注意USB批量传输的单次最大包长——高速模式下是512字节,超高速下是1024字节。
4.3 USB Gadget框架——Android设备侧的核心
既然是OTG课程,Gadget框架是重中之重。Android从3.4内核开始引入USB Gadget框架,后来迁移到了ConfigFS。我个人更喜欢ConfigFS的方式,因为用户空间可以直接配置USB功能,不用改死板的内核配置。
Gadget驱动栈有几张“功能卡”:
- f_mass_storage.ko:U盘模式,最常用也最简单。
- f_ecm.ko / f_rndis.ko:网络共享(USB绑定)。
- f_mtp.ko / f_ptp.ko:媒体传输协议。
- f_accessory.ko:Android附件模式,用于外设扩展。
- f_uvc.ko:USB视频设备类,4.4以后才有的。
曾经踩过的坑:我在一个项目里同时启用了MTP和RNDIS,结果MTP传输时网络经常卡顿。后来发现是Gadget的UDC驱动端点在争抢带宽——批量端点优先级比中断端点低,但MTP走批量,RNDIS走中断,优先级低的MTP被打断了。最终的解决是给MTP分配独立端点,并调整调度策略。
4.4 内核驱动层深探
内核层其实是整个系统最壮实的那根柱子。从功能上可以拆成三块:
- USB Core:通用的USB栈,包括设备枚举、控制传输、urb管理等。主机和设备侧都靠它。
- UDC(USB Device Controller)驱动:芯片厂商提供的硬件抽象层。高通叫
dwc3,瑞芯微用dwc2,有些MTK用mtu3。它们负责把内核的USB请求翻译成硬件寄存器操作。 - Gadget功能驱动:具体实现USB功能(MTP、ADB等)的内核模块。
数据流的实际路径是这样的:
应用层 (MTP App)
↓ 通过Binder调用
框架层 (UsbDeviceManager)
↓ 通过HAL
原生层 (libmtp.so → /dev/mtp_usb)
↓ 通过ConfigFS
内核层 (f_mtp.ko → gadget.ko → udc-core.ko)
↓ 通过DMA/寄存器
硬件层 (DWC3控制器 → USB PHY → 物理端口)
你可能会问,为什么Android要把USB搞得这么复杂?
说白了,为了安全和灵活。用户空间的App不能直接碰硬件;框架层负责权限管理和策略;内核层保证效率和稳定。每一层都加了一道锁,但也多了一个可能出bug的地方。
4.5 一张图看清全貌
下面的结构图把上面讲的四层框架和关键组件画出来了。重点关注每个层之间的接口——它们往往是调试的突破口。
4.6 主机侧 vs 设备侧——关键区别
这个课既然叫OTG,主机侧和设备侧都得讲。两者的内核栈长得不一样:
| 对比项 | 主机侧(Host) | 设备侧(Gadget/Peripheral) |
|---|---|---|
| 上层接口 | usb_device + 设备类驱动 |
usb_gadget + 功能驱动 |
| 核心模块 | OHCI/EHCI/XHCI | UDC(如dwc3.ko)+ Gadget层 |
| 电源管理 | 由Hub检测挂起/唤醒 | 由外接VBus变化触发 |
| 角色切换 | 仅从USB Core枚举 | 通过OTG ID引脚或软件切换 |
| 典型应用 | U盘、鼠标、键盘、摄像头 | MTP、ADB、RNDIS、UVC |
我在产品开发中经常遇到一个问题——设备插上PC后被识别为“未知USB设备”,那时我总是先看内核日志有没有枚举成功,再看Gadget功能是否正确加载。如果都没有问题,基本就是ConfigFS的符号链接没配好。
快速回忆:host/gadget模式切换靠的是 androidboot.mode 启动参数或者 sys/kernel/config/usb_gadget/ 下的配置。我自己习惯在init.rc里提前配好,避免adb在系统启动过程中断开重连。
4.7 小结:学习这张图的实用方法
如果你刚开始啃USB驱动,我给你三个建议:
- 先从内核层入手:打开
/sys/kernel/debug/usb/devices看看你的设备有没有被内核认出来。这是最底层的证据。 - 再追框架层日志:用
logcat -b all | grep -i usb抓取UsbService的日志,看看权限管理和策略切换是否正常。 - 最后才翻应用代码:从UsbManager API到HAL的调用链很容易看懂,但前提是底层没问题。
这张四层架构图你会反复用到。每当我遇到USB相关bug时,第一件事就是打开这张图,问自己:问题出在哪一层?然后逐层排查。掌握了这个思维,比记住一百个函数名都管用。