Android12 U盘插拔链路源码全解析(四):Framework层(上) —— UsbHostManager

系列目录第一篇:全景图与调用链路概览 | 第二篇:内核层---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=block uevent 是完全不同的通道。


四、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)
              → 数据输出(主机→设备)

这个树形结构被完整地序列化到 IntentEXTRA_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 意味着:

  1. 广播队列优先级最高,几乎即时送达
  2. 接收器运行在前台进程组
  3. 适合硬件热插拔这种对时效要求极高的场景

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 设备(分区/文件系统)
核心数据 UsbDeviceUsbInterfaceUsbEndpoint DiskPublicVolume
广播 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
相关推荐
qq3621967051 小时前
第三方安卓应用商店安全评测 2026:Appteka、Aptoide、APKPure 等 7 家横评
android·网络·人工智能·安全·chatgpt·智能手机
coderhuo2 小时前
JibarOS 简介:Android AICore 开源实现方案
android·ai编程
故渊at3 小时前
第十五板块:Android 系统调试与逆向工程 | 第三十六篇:Smali 字节码语义与 Dalvik 指令集
android·指令集·dalvik·smali·字节码语义
J2虾虾3 小时前
Android支持Java语言的标准
android·java·开发语言
charlee443 小时前
Unity在安卓端如何调试输出信息
android·unity·adb·游戏引擎·真机调试
法欧特斯卡雷特3 小时前
从 Kotlin 编译器 API 的变化开始: 2.4.0
android·开源·github
贾艺驰3 小时前
实战Android Framework: 新增一个系统服务
android·源码
火山上的企鹅3 小时前
Codex实战:APP远程升级服务搭建(五)App端远程升级接入
android·服务器·远程升级·qgc
BreezeDove3 小时前
【Android】Flutter3.35项目启动超时问题
android·flutter