系列目录 :第一篇:全景图与调用链路概览 | 第二篇:内核层---USB驱动与uevent | 第三篇:Native层---vold与NetlinkManager | 第四篇:Framework层(上)---UsbHostManager | 第五篇:Framework层(下)---StorageManagerService | 第六篇:广播分发与SystemUI响应 | 第七篇:应用层---MediaScanner与SAF | 第八篇:实战调试与案例分析
一、引言
前三篇我们走完了"存储挂载链"从 Kernel 到 vold 的路径。但还有一条 并行的 USB 设备感知链 完全独立运作:它不关心分区、不关心文件系统,只负责回答一个核心问题------"现在插着的是什么 USB 设备?"
这条链的主角就是 UsbHostManager。
本文将从 UsbService 启动、JNI 桥接、usbDeviceAttached() 全过程、UsbDevice 对象树构建,到广播发送,完整拆解 Java 框架层如何感知 USB 设备。
二、UsbService 启动链路
2.1 SystemServer 中的启动点
源码路径 :frameworks/base/services/java/com/android/server/SystemServer.java
java
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
// ...
t.traceBegin("StartUsbService");
mSystemServiceManager.startService(UsbService.Lifecycle.class);
t.traceEnd();
// ...
}
SystemServer 在 startBootstrapServices() 阶段启动 UsbService,这比大多数应用层服务都要早。
2.2 UsbService 构造函数
源码路径 :frameworks/base/services/usb/java/com/android/server/usb/UsbService.java
java
public class UsbService extends IUsbManager.Stub {
private UsbService(Context context) {
mContext = context;
mHasUsbAccessory = false;
mPackageManager = context.getPackageManager();
// ★ 创建 UsbHostManager(核心)
mHostManager = new UsbHostManager(context);
// ★ 创建 UsbDeviceManager(管理手机作为 USB 设备的角色)
mDeviceManager = new UsbDeviceManager(context);
// ★ 创建 UsbPortManager(管理 Type-C 端口角色切换)
mPortManager = new UsbPortManager(context);
// ★ 创建 UsbAlsaManager(USB 音频设备管理)
mAlsaManager = new UsbAlsaManager(context);
// ★ 创建 UsbSettingsManager(用户/应用 USB 权限管理)
mSettingsManager = new UsbSettingsManager(context);
}
// SystemService 的生命周期包装
public static class Lifecycle extends SystemService {
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
// ★ 在 AMS 就绪后,UsbHostManager 开始工作
mUsbService.systemReady();
}
}
}
public void systemReady() {
mHostManager.systemReady();
mDeviceManager.systemReady();
mPortManager.systemReady();
mAlsaManager.systemReady();
}
}
五大子模块职责:
| 模块 | 职责 |
|---|---|
UsbHostManager |
★ Host 模式核心:感知 USB 设备插入/拔出、广播 |
UsbDeviceManager |
Device 模式:手机作为 U 盘/充电等角色切换 |
UsbPortManager |
Type-C 端口管理:角色协商(DFP/UFP/DRP) |
UsbAlsaManager |
USB 音频设备(耳机、声卡)管理 |
UsbSettingsManager |
用户/应用 USB 权限授权管理 |
三、UsbHostManager 构造函数与 JNI 桥
3.1 构造函数
源码路径 :frameworks/base/services/usb/java/com/android/server/usb/UsbHostManager.java
java
public class UsbHostManager {
private static final String TAG = "UsbHostManager";
// ★ 存储当前连接的设备(以 deviceName 为 key)
private final HashMap<String, UsbDevice> mDevices = new HashMap<>();
// ★ 设备的 USB 类过滤黑名单
private final HashSet<Integer> mBlacklistedInterfaces;
private final HashSet<Integer> mBlacklistedDevices;
private final Context mContext;
private final Object mLock = new Object();
public UsbHostManager(Context context) {
mContext = context;
mBlacklistedInterfaces = new HashSet<>();
mBlacklistedDevices = new HashSet<>();
// 从资源文件读取黑名单配置
// 例如 HID 键盘/鼠标通常由 Input 系统处理,不需要 UsbHostManager 重复广播
String[] blacklist = context.getResources()
.getStringArray(com.android.internal.R.array.config_usbHostBlacklist);
for (String s : blacklist) {
// 解析黑名单规则(如 "class:0x03" 表示 HID 类)
}
}
}
3.2 systemReady() ------ JNI 监听启动
java
public void systemReady() {
synchronized (mLock) {
// ★ 启动 Native 层的 USB Host 总线监控
// 这个 JNI 方法会阻塞等待设备事件
new Thread("UsbService host thread") {
@Override
public void run() {
// ★ 核心!进入 JNI 层,循环等待 USB 事件
monitorUsbHostBus();
}
}.start();
}
}
3.3 JNI 层:monitorUsbHostBus()
源码路径 :frameworks/base/services/core/jni/com_android_server_UsbHostManager.cpp
cpp
static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv* env, jobject thiz) {
// ★ 打开 /dev/bus/usb 目录,用 inotify 或轮询监听变化
unique_fd fd(TEMP_FAILURE_RETRY(open(USB_FS_DIR, O_RDONLY | O_DIRECTORY)));
while (!isDeviceDeleted) {
// 1. 枚举所有 USB 总线
for (const auto& bus : scandir(USB_FS_DIR)) {
// 2. 枚举每个总线上的设备
for (const auto& dev : scandir(bus.path())) {
// 3. 读取设备描述符
struct usb_device_descriptor desc;
read(fd, &desc, sizeof(desc));
// 4. ★ 回调 Java 层
env->CallVoidMethod(thiz, method_usbDeviceAttached,
deviceName, // 如 "/dev/bus/usb/001/003"
desc.idVendor,
desc.idProduct,
desc.bDeviceClass,
desc.bDeviceSubClass,
desc.bDeviceProtocol,
manufacturerName,
productName,
version,
serialNumber);
}
}
// 5. 等待下一个设备变化事件(uevent 或 inotify)
poll(...);
}
}
关键点 :JNI 层并没有直接解析 uevent,而是通过 inotify 监听
/dev/bus/usb/目录 来感知设备变化。这与 vold 通过 netlink socket 监听SUBSYSTEM=blockuevent 是完全不同的通道。
四、usbDeviceAttached() ------ 完整源码拆解
这是 UsbHostManager 中 最重要的方法,也是最长的单个方法之一。
源码路径 :frameworks/base/services/usb/java/com/android/server/usb/UsbHostManager.java
java
// ★ JNI 回调入口
private void usbDeviceAttached(String deviceName, int vendorID, int productID,
int deviceClass, int deviceSubclass, int deviceProtocol,
String manufacturerName, String productName, String version,
String serialNumber) {
// ========== 第一步:获取设备完整信息 ==========
// 通过 nativeGetDeviceMembers() 获取接口、端点等详细信息
UsbDescriptorParser.DeviceDescriptor deviceDesc =
nativeGetDeviceMembers(deviceName);
// ========== 第二步:黑名单过滤 ==========
// 过滤掉不需要 UsbHostManager 处理的设备
// 例如 HID 键盘/鼠标已经由 InputFlinger 接管
if (shouldBlackList(deviceClass, deviceSubclass, deviceProtocol,
manufacturerName, productName, version, serialNumber)) {
return; // ★ 被黑名单拦截,直接返回
}
// ========== 第三步:构建接口和端点描述符 ==========
int numInterfaces = deviceDesc.getNumInterfaces();
UsbInterface[] interfaces = new UsbInterface[numInterfaces];
for (int i = 0; i < numInterfaces; i++) {
UsbDescriptorParser.InterfaceDescriptor ifaceDesc =
deviceDesc.getInterface(i);
// 检查接口是否在黑名单中
if (shouldBlackList(ifaceDesc.getInterfaceClass(),
ifaceDesc.getInterfaceSubclass(),
ifaceDesc.getInterfaceProtocol(), null, null, null, null)) {
// 该接口被黑名单拦截,但设备本身可能还有其他可用接口
ifaceDesc.setBlacklisted(true);
}
// 读取接口的供应商特定描述符
byte[] rawDescs = deviceDesc.getRawDescriptors(i);
// 构建端点数组
int numEndpoints = ifaceDesc.getNumEndpoints();
UsbEndpoint[] endpoints = new UsbEndpoint[numEndpoints];
for (int e = 0; e < numEndpoints; e++) {
UsbDescriptorParser.EndpointDescriptor endpointDesc =
ifaceDesc.getEndpoint(e);
endpoints[e] = new UsbEndpoint(
endpointDesc.getEndpointAddress(), // 如 0x81(IN 端点)
endpointDesc.getAttributes(), // 如 0x02(Bulk 传输)
endpointDesc.getMaxPacketSize(), // 最大包大小
endpointDesc.getInterval() // 轮询间隔
);
}
// ★ 构建 UsbInterface 对象
interfaces[i] = new UsbInterface(
ifaceDesc.getInterfaceNumber(),
ifaceDesc.getAlternateSetting(),
manufacturerName,
productName,
version,
serialNumber,
ifaceDesc.getInterfaceClass(),
ifaceDesc.getInterfaceSubclass(),
ifaceDesc.getInterfaceProtocol(),
endpoints,
rawDescs
);
}
// ========== 第四步:构建 UsbDevice 对象树 ==========
UsbDevice device = new UsbDevice(
deviceName,
vendorID, // 厂商 ID,如 0x0781 (SanDisk)
productID, // 产品 ID,如 0x5591
deviceClass, // 设备类
deviceSubclass,
deviceProtocol,
manufacturerName, // "SanDisk"
productName, // "Ultra USB 3.0"
version, // USB 版本
serialNumber, // 序列号
null, // 配置(可为 null)
interfaces, // ★ 接口数组
null, null // 控制端点
);
// ========== 第五步:加入设备列表并发送广播 ==========
synchronized (mLock) {
// 5.1 检查是否重复
if (mDevices.get(deviceName) != null) {
Slog.w(TAG, "device already on mDevices list: " + deviceName);
return;
}
// 5.2 加入 HashMap
mDevices.put(deviceName, device);
// 5.3 ★ 发送广播 ACTION_USB_DEVICE_ATTACHED
Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
}
4.1 黑名单过滤机制
java
// config.xml 中配置的黑名单示例
// frameworks/base/core/res/res/values/config.xml
<string-array name="config_usbHostBlacklist">
<!-- HID (Human Interface Device) - handled by InputFlinger -->
<item>class:0x03</item>
<!-- ADB Interface - handled by adbd -->
<item>subclass:0x02,protocol:0x01</item>
</string-array>
黑名单规则支持以下匹配方式(任意匹配一项即拦截):
| 规则格式 | 含义 | 示例 |
|---|---|---|
class:0xXX |
按设备类匹配 | class:0x03 = HID |
subclass:0xXX |
按子类匹配 | subclass:0x02 |
protocol:0xXX |
按协议匹配 | protocol:0x01 |
vid:0xXXXX |
按厂商 ID 匹配 | vid:0x046d = Logitech |
五、UsbDevice 对象树结构
构建完成后的对象树层级关系:
UsbDevice (SanDisk Ultra USB 3.0)
│ vendorID=0x0781, productID=0x5591
│ deviceClass=0x00, serialNumber="4C530001260121106363"
│
└── UsbInterface[0] (interfaceClass=0x08 Mass Storage)
│ interfaceSubclass=0x06 (SCSI)
│ interfaceProtocol=0x50 (Bulk-Only)
│
├── UsbEndpoint (address=0x81, type=BULK, IN)
│ → 数据输入(设备→主机)
│
└── UsbEndpoint (address=0x02, type=BULK, OUT)
→ 数据输出(主机→设备)
这个树形结构被完整地序列化到 Intent 的 EXTRA_DEVICE 中,通过 Binder 跨进程传递到接收广播的应用。
六、广播发送分析
6.1 Intent 构建
java
// UsbManager.ACTION_USB_DEVICE_ATTACHED
// 实际值: "android.hardware.usb.action.USB_DEVICE_ATTACHED"
Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
// ★ 携带完整的 UsbDevice 对象(实现了 Parcelable)
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
// ★ 标记为前台广播(更快送达)
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
// ★ 发送给所有用户
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
6.2 FLAG_RECEIVER_FOREGROUND 的含义
| Flag | 优先级 | 适用场景 |
|---|---|---|
| 无 Flag(普通广播) | 低 | 通用事件 |
FLAG_RECEIVER_FOREGROUND |
高 | 实时性要求高的硬件事件 |
FLAG_RECEIVER_REGISTERED_ONLY |
- | 仅动态注册的接收器 |
使用 FLAG_RECEIVER_FOREGROUND 意味着:
- 广播队列优先级最高,几乎即时送达
- 接收器运行在前台进程组
- 适合硬件热插拔这种对时效要求极高的场景
6.3 拔出流程 ------ usbDeviceDetached()
java
private void usbDeviceDetached(String deviceName) {
synchronized (mLock) {
UsbDevice device = mDevices.remove(deviceName);
if (device != null) {
Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
}
}
拔出流程非常简单:从 mDevices 中移除、发送 ACTION_USB_DEVICE_DETACHED 广播。
七、与 vold 的并行对比
现在可以清晰地看到两条并行链路的设计差异:
| 维度 | UsbHostManager 链路 | vold + StorageManagerService 链路 |
|---|---|---|
| 感知方式 | JNI inotify 监听 /dev/bus/usb/ |
netlink socket 监听 uevent |
| 关注对象 | USB 设备本身(vendor/product/class) | Block 设备(分区/文件系统) |
| 核心数据 | UsbDevice → UsbInterface → UsbEndpoint |
Disk → PublicVolume |
| 广播 | USB_DEVICE_ATTACHED / DETACHED |
MEDIA_MOUNTED / MEDIA_UNMOUNTED |
| 主要消费者 | 需要直接操作 USB 设备的 App | 需要读写文件的 App / MediaScanner |
| 典型场景 | USB 摄像头、USB 串口、自定义 USB 配件 | U 盘、移动硬盘 |
八、关键源码文件索引
frameworks/base/services/
├── java/com/android/server/SystemServer.java
│ → startBootstrapServices() 中启动 UsbService
│
├── usb/java/com/android/server/usb/
│ ├── UsbService.java
│ │ → 五大子模块的容器
│ ├── UsbHostManager.java ★ 本文主角
│ │ → usb