Android 的 USB 连接可同时服务三类完全不同的使用场景:开发者通过 ADB 调试应用、普通用户通过 MTP 传输照片、配件厂商通过 USB 主机模式接入游戏手柄。每一类场景都使用软件栈中独立的模块,该软件栈覆盖从用户空间 Java 服务,一直到 Linux 内核的 USB 小工具(gadget)与主机控制器驱动。本章追踪 USB 线路上每一字节数据的流向,依次经过 HAL 层、框架层服务,最终抵达应用层,全文会引用真实的 AOSP 源码路径。
39.1 USB 框架概述
39.1.1 整体架构概览
Android USB 子系统划分为 4 个垂直层级:公开 SDK API(UsbManager)、系统服务(UsbService 及其子管理器)、硬件抽象层(AIDL HAL 接口 IUsb、IUsbGadget)、Linux 内核 USB 子系统(gadget 驱动、主机控制器驱动、configfs、functionfs)。
39.1.2 核心组件
| 组件 | 类型 | 源码路径 | 作用 | ||||
|---|---|---|---|---|---|---|---|
| UsbManager | SDK API | frameworks/base/core/java/android/hardware/usb/UsbManager.java | 应用使用的公开 API | ||||
| UsbService | 系统服务 | frameworks/base/services/usb/.../UsbService.java | 核心协调器 | ||||
| UsbDeviceManager | 内部管理器 | frameworks/base/services/usb/.../UsbDeviceManager.java | Gadget 模式状态机 | ||||
| UsbHostManager | 内部管理器 | frameworks/base/services/usb/.../UsbHostManager.java | 主机模式设备枚举 | ||||
| UsbPortManager | 内部管理器 | frameworks/base/services/usb/.../UsbPortManager.java | Type‑C 端口管理 | ||||
| UsbPermissionManager | 内部管理器 | frameworks/base/services/usb/.../UsbPermissionManager.java | 按用户跟踪权限 | ||||
| IUsb | AIDL HAL | hardware/interfaces/usb/aidl/.../IUsb.aidl | 端口状态、角色切换 | ||||
| IUsbGadget | AIDL HAL | hardware/interfaces/usb/gadget/aidl/.../IUsbGadget.aidl | Gadget 功能配置 | ||||
| adbd | 原生守护进程 | packages/modules/adb/daemon/main.cpp | ADB 守护进程 | ||||
| MTP Native | 原生库 | frameworks/av/media/mtp/ | MTP 协议实现 | MTP Service | Java 服务 | packages/services/Mtp/ | MTP 文档提供者 |
39.1.3 双模式架构:Gadget 模式与主机模式
同一个 USB Type‑C 端口可工作在两种完全不同的模式,模式由 USB 供电协议协商得到的数据角色决定:
设备 / Gadget 模式(UFP):Android 设备作为外设接入主机(通常是 PC)。支持 MTP 文件传输、ADB 调试、PTP 照片传输、RNDIS 网络共享、MIDI、USB 配件 AOA。内核 USB gadget 框架(configfs)对外提供复合 USB 功能。
主机模式(DFP):Android 设备充当 USB 主机。会枚举接入的 USB 外设(键盘、鼠标、存储、音频设备),并通过 UsbManager API 向应用提供访问。
UsbPortManager 通过 IUsb HAL 监听端口状态变更,协调两种模式之间的切换。
39.1.4 UsbManager —— 公开 API
UsbManager(源码:frameworks/base/core/java/android/hardware/usb/UsbManager.java)带有@SystemService注解,是应用与 USB 交互的入口。它提供:
设备 (gadget) 模式操作:
- 查询与设置当前 USB 功能(MTP、PTP 等)
- 获取 USB 配件信息
- 打开 USB 配件连接
主机模式操作:
- 枚举已连接 USB 设备(
getDeviceList()) - 请求设备通信权限
- 打开设备连接(
openDevice())
功能常量定义可用的 gadget 配置:
// 摘自UsbManager.java —— 功能位掩码 public static final long FUNCTION_NONE = 0; public static final long FUNCTION_MTP = GadgetFunction.MTP; // 1 << 2 public static final long FUNCTION_PTP = GadgetFunction.PTP; // 1 << 4 public static final long FUNCTION_RNDIS = GadgetFunction.RNDIS; // 1 << 5 public static final long FUNCTION_MIDI = GadgetFunction.MIDI; // 1 << 3 public static final long FUNCTION_ACCESSORY = GadgetFunction.ACCESSORY; // 1 << 1 public static final long FUNCTION_AUDIO_SOURCE = GadgetFunction.AUDIO_SOURCE; // 1 << 6 public static final long FUNCTION_ADB = GadgetFunction.ADB; // 1 public static final long FUNCTION_NCM = GadgetFunction.NCM; // 1 << 10 public static final long FUNCTION_UVC = GadgetFunction.UVC; // 1 << 7这些常量直接映射 AIDL 可序列化对象 GadgetFunction,定义位置:hardware/interfaces/usb/gadget/aidl/android/hardware/usb/gadget/GadgetFunction.aidl。
39.1.5 UsbService —— 核心协调器
UsbService(源码:frameworks/base/services/usb/java/com/android/server/usb/UsbService.java)实现 IUsbManager,运行在 system_server 进程。它是所有 USB 操作的 Binder 服务端点,将工作委派给各个专用子管理器:
UsbAuthManager 仅在enableUsbHostAuthorization标记开启时才会实例化(参考 UsbService.java 中mAuthManager = new UsbAuthManager(...)附近代码);它对接独立进程 Rust 守护进程,详见 39.10 节。
服务生命周期遵循标准 SystemService 模式:
- 构造阶段:system_server 启动时实例化 UsbService。
- systemReady():触发所有子管理器初始化。UsbHostManager 启动原生线程监听
/dev/bus/usb设备插拔事件。UsbPortManager 向 HAL 查询当前端口状态。 - 运行阶段:处理来自应用的 Binder 调用,广播 USB 状态变更,按用户配置管理权限与设置。
39.1.6 系统属性与 Sysfs 路径
UsbDeviceManager 通过多个内核接口与系统属性监控、控制 USB 状态:
| 接口 | 路径 | 用途 | ||||||
|---|---|---|---|---|---|---|---|---|
| USB 状态 sysfs | /sys/class/android_usb/android0/state | 旧版 gadget 状态 | ||||||
| USB 功能 sysfs | /sys/class/android_usb/android0/functions | 旧版功能配置 | ||||||
| UDC 控制器 | sys.usb.controller(系统属性) | ConfigFS UDC 名称 | ||||||
| USB 配置 | persist.sys.usb.config(系统属性) | 持久化 USB 配置 | RNDIS 地址 | /sys/class/android_usb/android0/f_rndis/ethaddr | 网络共享 MAC 地址 | MIDI ALSA | /sys/class/android_usb/android0/f_midi/alsa | MIDI 设备信息 |
| UEvent 匹配 | DEVPATH=/devices/virtual/android_usb/android0 | 旧版状态变更 | ||||||
| UEvent 匹配 | SUBSYSTEM=udc | 现代 UDC 状态变更 | ||||||
| FunctionFS | /dev/usb‑ffs/adb/ | ADB FunctionFS 端点 |
39.2 UsbDeviceManager:Gadget 模式状态机
39.2.1 概述
UsbDeviceManager(源码:frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java)是 USB 框架中最复杂的组件。它管理 Android 设备作为 USB 外设的行为,处理功能切换(MTP、PTP、RNDIS、配件、MIDI、ADB)、线缆事件触发的状态迁移,同时协调锁屏状态、用户偏好、内核层 USB 配置。
该类实现ActivityTaskManagerInternal.ScreenObserver,用于响应锁屏状态变更。关键点:MTP 访问用户存储数据要求设备屏幕处于解锁状态。
39.2.2 架构
39.2.3 双 Handler 策略
UsbDeviceManager 在构造时选择两种具体 Handler 实现之一:
// 摘自UsbDeviceManager构造函数 if (mUsbGadgetHal == null) { // 初始化传统UsbHandler mHandler = new UsbHandlerLegacy(FgThread.get().getLooper(), mContext, this, alsaManager, permissionManager); } else { // 初始化基于HAL的UsbHandler mHandler = new UsbHandlerHal(FgThread.get().getLooper(), mContext, this, alsaManager, permissionManager); }- UsbHandlerHal:现代设备使用,设备具备 IUsbGadget AIDL HAL。调用 HAL 的
setCurrentUsbFunctions()请求配置变更,HAL 内部完成内核 ConfigFS 操作。 - UsbHandlerLegacy:旧设备无 gadget HAL 时的回退方案。直接写 sysfs 文件与系统属性切换 USB 功能。
39.2.4 基于消息的状态机
UsbHandler 通过 Android Handler 消息队列处理 USB 状态迁移。所有状态变更串行在前台线程执行,避免竞态条件。
| 消息 ID | 常量 | 触发来源 |
|---|---|---|
| 0 | MSG_UPDATE_STATE | 内核上报连接 / 断开 / 配置完成 |
| 1 | MSG_ENABLE_ADB | 开发者选项开关 ADB |
| 2 | MSG_SET_CURRENT_FUNCTIONS | 应用请求切换功能 |
| 3 | MSG_SYSTEM_READY | system server 就绪 |
| 4 | MSG_BOOT_COMPLETED | 开机完成广播 |
| 5 | MSG_USER_SWITCHED | 切换当前用户 |
| 6 | MSG_UPDATE_USER_RESTRICTIONS | 用户策略变更 |
| 7 | MSG_UPDATE_PORT_STATE | Type‑C 端口状态变更 |
| 8 | MSG_ACCESSORY_MODE_ENTER_TIMEOUT | 配件协商 10 秒超时 |
| 9 | MSG_UPDATE_CHARGING_STATE | 电池充电状态变更 |
| 10 | MSG_UPDATE_HOST_STATE | 主机模式设备插拔 |
| 11 | MSG_LOCALE_CHANGED | 语言变更(更新通知) |
| 12 | MSG_SET_SCREEN_UNLOCKED_FUNCTIONS | 屏幕解锁时功能偏好 |
| 13 | MSG_UPDATE_SCREEN_LOCK | 锁屏显示 / 隐藏 |
| 14 | MSG_SET_CHARGING_FUNCTIONS | 切换至仅充电模式 |
| 15 | MSG_SET_FUNCTIONS_TIMEOUT | 功能切换超时 |
| 16 | MSG_GET_CURRENT_USB_FUNCTIONS | 查询当前 gadget 功能 |
| 17 | MSG_FUNCTION_SWITCH_TIMEOUT | gadget 重新枚举超时 |
| 18 | MSG_GADGET_HAL_REGISTERED | HAL 服务就绪 |
| 19 | MSG_RESET_USB_GADGET | 重置 gadget 硬件 |
| 20 | MSG_ACCESSORY_HANDSHAKE_TIMEOUT | AOA 握手超时 |
| 21 | MSG_INCREASE_SENDSTRING_COUNT | 收到 AOA 字符串描述符 |
| 22 | MSG_UPDATE_USB_SPEED | USB 速率协商完成 |
| 23 | MSG_UPDATE_HAL_VERSION | HAL 版本信息更新 |
| 24 | MSG_USER_UNLOCKED_AFTER_BOOT | 开机后首次解锁 |
39.2.5 USB 状态迁移
内核通过 UEvent 消息上报 USB 状态变更。UsbUEventObserver 接收消息并转换为 Handler 消息。
UsbHandler 的updateState()将内核状态字符串映射为内部状态:
// 摘自UsbHandler.updateState() if ("DISCONNECTED".equals(state)) { connected = 0; configured = 0; } else if ("CONNECTED".equals(state)) { connected = 1; configured = 0; } else if ("CONFIGURED".equals(state)) { connected = 1; configured = 1; }39.2.6 功能切换
用户或系统请求切换 USB 功能时,状态机执行多步流程:
39.2.7 防抖与超时
功能切换会带来短暂 USB 断开。状态机会做防抖处理,避免虚假断开事件干扰切换流程。
// UsbDeviceManager中防抖延时 private static final int DEVICE_STATE_UPDATE_DELAY_EXT = 3000; // 3秒 private static final int DEVICE_STATE_UPDATE_DELAY = 1000; // 1秒 private static final int HOST_STATE_UPDATE_DELAY = 1000; // 1秒 private static final int ACCESSORY_REQUEST_TIMEOUT = 10 * 1000; // 10秒 private static final int ACCESSORY_HANDSHAKE_TIMEOUT = 10 * 1000; // 10秒调用resetUsbGadget()之后,通过mResetUsbGadgetDisableDebounce临时关闭防抖,保证 gadget 重置后的第一次断开事件立即处理。
39.2.8 与锁屏交互
MTP 需要访问用户存储,锁屏时必须做保护。UsbDeviceManager 与锁屏模块协同工作:
- 屏幕锁定(
onKeyguardStateChanged(true)),Handler 收到 MSG_UPDATE_SCREEN_LOCK。 - 如果 MTP 或 PTP 处于激活状态,Handler 切换为仅充电功能。
- 用户偏好功能保存在 SharedPreferences,key 为
usb‑screen‑unlocked‑config‑<userId>。 - 屏幕解锁后,恢复之前保存的 USB 功能。
39.2.9 接口黑名单
出于安全考虑,当设备作为 USB 主机时,部分 USB 接口类禁止应用访问:
// 摘自UsbDeviceManager静态代码块 sDenyInterfaces.add(UsbConstants.USB_CLASS_AUDIO); sDenyInterfaces.add(UsbConstants.USB_CLASS_COMM); sDenyInterfaces.add(UsbConstants.USB_CLASS_HID); sDenyInterfaces.add(UsbConstants.USB_CLASS_PRINTER); sDenyInterfaces.add(UsbConstants.USB_CLASS_MASS_STORAGE); sDenyInterfaces.add(UsbConstants.USB_CLASS_HUB); sDenyInterfaces.add(UsbConstants.USB_CLASS_CDC_DATA); sDenyInterfaces.add(UsbConstants.USB_CLASS_CSCID); sDenyInterfaces.add(UsbConstants.USB_CLASS_CONTENT_SEC); sDenyInterfaces.add(UsbConstants.USB_CLASS_VIDEO); sDenyInterfaces.add(UsbConstants.USB_CLASS_WIRELESS_CONTROLLER);39.2.10 MTP 服务绑定
MTP/PTP 功能激活时,Handler 绑定 MTP 服务:
// UsbHandler常量 protected static final String MTP_PACKAGE_NAME = "com.android.mtp"; protected static final String MTP_SERVICE_CLASS_NAME = "com.android.mtp.MtpService";ServiceConnection 在 MTP 会话整个生命周期保持绑定;切换离开 MTP/PTP 时解除绑定。该绑定除了启动服务外还有关键作用:阻止 ActivityManager 冻结 MTP 进程。如果没有绑定,系统可能为回收资源冻结 MTP 服务进程,打断正在进行的 USB 传输。
39.2.11 MIDI 功能探测
MIDI gadget 功能启用后,UsbDeviceManager 需要探测合成 MIDI 设备对应的 ALSA 卡号与设备号,有两种实现方式。
现代方案(基于 sysfs 解析):
// 遍历UDC控制器下sysfs目录 File soundDir = new File("/sys/class/udc/" + controllerName + "/gadget/sound"); File[] cardDirs = FileUtils.listFilesOrEmpty(soundDir, (dir, file) -> file.startsWith("card")); File[] midis = FileUtils.listFilesOrEmpty(cardDirs[0], (dir, file) -> file.startsWith("midi")); // 从"midiC<card>D<device>"解析卡号设备号 Pattern pattern = Pattern.compile("midiC(\\d+)D(\\d+)"); Matcher matcher = pattern.matcher(midis[0].getName()); if (matcher.matches()) { mMidiCard = Integer.parseInt(matcher.group(1)); mMidiDevice = Integer.parseInt(matcher.group(2)); }传统方案(读取 ALSA 文件):
// 读取旧版sysfs路径 Scanner scanner = new Scanner(new File(MIDI_ALSA_PATH)); mMidiCard = scanner.nextInt(); mMidiDevice = scanner.nextInt();探测得到的卡号、设备号传给 UsbAlsaManager,向 Android MIDI 服务注册外设 MIDI 设备。
39.2.12 USB 状态广播
Handler 向所有接收者广播 USB 状态变更:
protected void updateUsbStateBroadcastIfNeeded(long functions) { Intent intent = new Intent(UsbManager.ACTION_USB_STATE); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(UsbManager.USB_CONNECTED, mConnected); intent.putExtra(UsbManager.USB_HOST_CONNECTED, mHostConnected); intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured); intent.putExtra(UsbManager.USB_DATA_UNLOCKED, isUsbTransferAllowed() && isUsbDataTransferActive(mCurrentFunctions)); // 添加激活功能标记 long remainingFunctions = functions; while (remainingFunctions != 0) { intent.putExtra(UsbManager.usbFunctionsToString( Long.highestOneBit(remainingFunctions)), true); remainingFunctions -= Long.highestOneBit(remainingFunctions); } // 仅状态真正变化才广播 if (!isUsbStateChanged(intent)) return; sendStickyBroadcast(intent); }ACTION_USB_STATE是粘性广播:晚注册的接收者会立刻收到上一次广播状态。Intent 携带各个激活功能的布尔值,接收方可以单独检查某一个功能状态。
39.2.13 用户限制执行
企业管控设备可通过UserManager.DISALLOW_USB_FILE_TRANSFER限制 USB 文件传输:
protected boolean isUsbTransferAllowed() { UserManager userManager = (UserManager) mContext.getSystemService( Context.USER_SERVICE); return !userManager.hasUserRestriction( UserManager.DISALLOW_USB_FILE_TRANSFER); }该限制生效时:
- MTP、PTP 功能被屏蔽
- USB 通知显示 “仅充电”
- 应用无法切换至数据传输功能
39.2.14 AOA 配件握手跟踪
Handler 记录 AOA 握手过程详细时序信息,用于调试与统计:
private long mAccessoryConnectionStartTime = 0L; // 收到GET_PROTOCOL时刻 private int mSendStringCount = 0; // SEND_STRING uevent计数 private boolean mStartAccessory = false; // 是否收到START // 广播握手详情用于调试 private void broadcastUsbAccessoryHandshake() { Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_HANDSHAKE) .putExtra(UsbManager.EXTRA_ACCESSORY_UEVENT_TIME, mAccessoryConnectionStartTime) .putExtra(UsbManager.EXTRA_ACCESSORY_STRING_COUNT, mSendStringCount) .putExtra(UsbManager.EXTRA_ACCESSORY_START, mStartAccessory) .putExtra(UsbManager.EXTRA_ACCESSORY_HANDSHAKE_END, SystemClock.elapsedRealtime()); sendStickyBroadcast(intent); }39.2.15 RNDIS 网络共享集成
RNDIS(USB 网络共享)激活时:
- Handler 配置 RNDIS gadget 功能。
- 基于
ro.serialno生成本地管理 MAC 地址:
// 首字节设0x02,表示本地管理地址 address[0] = 0x02; String serial = SystemProperties.get("ro.serialno", "1234567890ABCDEF"); // USB序列号异或填充剩余5字节 for (int i = 0; i < serialLength; i++) { address[i % (ETH_ALEN - 1) + 1] ^= (int) serial.charAt(i); }