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

系列目录第一篇:全景图与调用链路概览 | 第二篇:内核层---USB驱动与uevent | 第三篇:Native层---vold与NetlinkManager | 第四篇:Framework层(上)---UsbHostManager | 第五篇:Framework层(下)---MountService | 第六篇:广播分发与SystemUI响应 | 第七篇:应用层---MediaScanner与SAF | 第八篇:实战调试与案例分析


一、引言

前三篇我们走完了"存储挂载链"从 Kernel 到 vold 的路径。但还有一条并行的 USB 设备感知链完全独立运作:它不关心分区、不关心文件系统,只负责回答一个核心问题------"现在插着的是什么 USB 设备?"

这条链的主角就是 UsbHostManager

本文基于 Android 7.1.2(Nougat, API 25, build N2G47H) 源码,从 UsbService 启动、JNI 桥接、beginUsbDeviceAdded() 分段回调全过程、UsbDevice 对象树构建,到广播发送,完整拆解 Java 框架层如何感知 USB 设备。

架构特点 :Android 7 的 JNI 层采用分段回调模式beginUsbDeviceAdded → addUsbConfiguration → addUsbInterface → addUsbEndpoint → endUsbDeviceAdded),逐步构建 UsbDevice 对象树。另外,广播的发送不在 UsbHostManager 中,而是在 UsbSettingsManager 中完成。


二、UsbService 启动链路

2.1 SystemServer 中的启动点

源码路径frameworks/base/services/java/com/android/server/SystemServer.java

java 复制代码
// Line 1040-1046
if (!disableUsb && hasUsbHostOrAccessory) {
    // Manage USB host and device support
    Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartUsbService");
    mSystemServiceManager.startService(USB_SERVICE_CLASS);
    Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}

SystemServer 在 startBootstrapServices() 阶段通过 SystemServiceManager 启动 UsbService。启动有一个前置条件:设备必须支持 USB Host 或 USB Accessory(hasUsbHostOrAccessory)。

2.2 UsbService 构造函数

源码路径frameworks/base/services/usb/java/com/android/server/usb/UsbService.java

java 复制代码
public class UsbService extends IUsbManager.Stub {

    public static class Lifecycle extends SystemService {
        private UsbService mUsbService;

        public Lifecycle(Context context) {
            super(context);
        }

        @Override
        public void onStart() {
            mUsbService = new UsbService(getContext());
            publishBinderService(Context.USB_SERVICE, mUsbService);
        }

        @Override
        public void onBootPhase(int phase) {
            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
                mUsbService.systemReady();
            } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
                mUsbService.bootCompleted();
            }
        }
    }

    private final UsbAlsaManager mAlsaManager;
    private UsbDeviceManager mDeviceManager;
    private UsbHostManager mHostManager;
    private UsbPortManager mPortManager;
    private final SparseArray<UsbSettingsManager> mSettingsByUser =
            new SparseArray<UsbSettingsManager>();

    public UsbService(Context context) {
        mContext = context;
        mAlsaManager = new UsbAlsaManager(context);

        final PackageManager pm = mContext.getPackageManager();
        // ★ 条件创建:只有支持 USB Host 的设备才创建 UsbHostManager
        if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
            mHostManager = new UsbHostManager(context, mAlsaManager);
        }
        // ★ 条件创建:只有存在 /sys/class/android_usb 的设备才创建 UsbDeviceManager
        if (new File("/sys/class/android_usb").exists()) {
            mDeviceManager = new UsbDeviceManager(context, mAlsaManager);
        }
        if (mHostManager != null || mDeviceManager != null) {
            mPortManager = new UsbPortManager(context);
        }

        setCurrentUser(UserHandle.USER_SYSTEM);

        // 注册用户切换和策略变更广播
        final IntentFilter filter = new IntentFilter();
        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
        filter.addAction(Intent.ACTION_USER_SWITCHED);
        filter.addAction(Intent.ACTION_USER_STOPPED);
        filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
        mContext.registerReceiver(mReceiver, filter, null, null);
    }
}

设计要点:

  • 通过 FEATURE_USB_HOST/sys/class/android_usb 文件存在性条件创建子模块,而非无条件创建
  • UsbSettingsManager 是**按用户(per-user)**存储的(SparseArray<UsbSettingsManager>,以 int userId 为 key)
  • UsbHostManager 从外部接收 UsbAlsaManager 引用,而不是自己创建

2.3 systemReady() 触发

java 复制代码
public void systemReady() {
    mAlsaManager.systemReady();
    if (mDeviceManager != null) {
        mDeviceManager.systemReady();
    }
    if (mHostManager != null) {
        mHostManager.systemReady();  // ★ 核心:启动 JNI 监听线程
    }
    if (mPortManager != null) {
        mPortManager.systemReady();
    }
}

三、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.class.getSimpleName();
    private static final boolean DEBUG = false;  // ★ 默认关闭 debug

    // ★ 存储当前连接的设备(以 deviceName 为 key)
    private final HashMap<String, UsbDevice> mDevices = new HashMap<String, UsbDevice>();

    // ★ USB 总线黑名单(字符串前缀匹配)
    private final String[] mHostBlacklist;

    private final Context mContext;
    private final Object mLock = new Object();

    // ★ 分段构建设备时的临时变量
    private UsbDevice mNewDevice;
    private UsbConfiguration mNewConfiguration;
    private UsbInterface mNewInterface;
    private ArrayList<UsbConfiguration> mNewConfigurations;
    private ArrayList<UsbInterface> mNewInterfaces;
    private ArrayList<UsbEndpoint> mNewEndpoints;

    private final UsbAlsaManager mUsbAlsaManager;

    @GuardedBy("mLock")
    private UsbSettingsManager mCurrentSettings;

    public UsbHostManager(Context context, UsbAlsaManager alsaManager) {
        mContext = context;
        mHostBlacklist = context.getResources().getStringArray(
                com.android.internal.R.array.config_usbHostBlacklist);
        mUsbAlsaManager = alsaManager;
    }
}

关键点DEBUG = false 意味着所有 Slog.d() 调试日志默认不输出,这在实际问题排查中增加了难度。当 DEBUG 关闭时,usbDeviceRemoved() 的调用日志无法在 logcat 中看到,只能通过间接证据(如应用层收到的 DETACHED 广播)来推断它被调用了。

3.2 systemReady() ------ JNI 监听启动

java 复制代码
public void systemReady() {
    synchronized (mLock) {
        // ★ 创建独立线程,进入 JNI 层循环等待 USB 事件
        Runnable runnable = new Runnable() {
            public void run() {
                monitorUsbHostBus();  // ★ 阻塞调用,永不返回
            }
        };
        new Thread(null, runnable, "UsbService host thread").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)
{
    // ★ 使用 libusbhost 初始化 USB Host 上下文
    struct usb_host_context* context = usb_host_init();
    if (!context) {
        ALOGE("usb_host_init failed");
        return;
    }
    // ★ 这个调用永远不会返回,所以可以直接传递 thiz 作为回调参数
    usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
}

usb_host_run() 的内部机制 (libusbhost 库,位于 system/core/libusbhost/):

  1. 打开 /dev/bus/usb/ 目录并枚举所有现有设备
  2. 使用 inotify 监听 /dev/bus/usb/ 目录的 IN_CREATE/IN_DELETE 事件
  3. 发现新设备时调用 usb_device_added() 回调
  4. 发现设备移除时调用 usb_device_removed() 回调

注意 :libusbhost 使用 inotify 而非 uevent/netlink 来感知 USB 设备变化。usb_host_init() 内部调用 inotify_init()usb_host_run()usb_host_load() 通过 inotify_add_watch() 注册对 /dev/dev/bus/dev/bus/usb/ 等目录的监听。

3.4 JNI 层:usb_device_added() ------ 分段回调

Android 7 的 JNI 层不是一次性把设备信息传给 Java 层,而是逐步解析每个 USB 描述符,逐个回调 Java 方法,采用分段构建设备对象树的模式:

cpp 复制代码
// ★ 注册的 Java 回调方法 ID
static jmethodID method_beginUsbDeviceAdded;   // 1. 开始添加设备
static jmethodID method_addUsbConfiguration;   // 2. 添加配置描述符
static jmethodID method_addUsbInterface;       // 3. 添加接口描述符
static jmethodID method_addUsbEndpoint;        // 4. 添加端点描述符
static jmethodID method_endUsbDeviceAdded;     // 5. 完成设备添加
static jmethodID method_usbDeviceRemoved;      // 6. 设备移除

static int usb_device_added(const char *devname, void* client_data) {
    // ====== 第一步:打开 USB 设备 ======
    struct usb_device *device = usb_device_open(devname);
    if (!device) {
        ALOGE("usb_device_open failed\n");
        return 0;
    }

    JNIEnv* env = AndroidRuntime::getJNIEnv();
    jobject thiz = (jobject)client_data;
    const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);

    // ====== 第二步:读取设备字符串描述符 ======
    char *manufacturer = usb_device_get_manufacturer_name(device);
    char *product = usb_device_get_product_name(device);
    int version = usb_device_get_version(device);
    char *serial = usb_device_get_serial(device);

    // ====== 第三步:回调 Java ------ beginUsbDeviceAdded() ======
    jboolean result = env->CallBooleanMethod(thiz, method_beginUsbDeviceAdded,
            deviceName, vendorID, productID,
            deviceClass, deviceSubclass, deviceProtocol,
            manufacturerName, productName, version, serialNumber);

    if (!result) goto fail;  // ★ 黑名单拦截或重复设备,停止解析

    // ====== 第四步:遍历所有描述符,逐个回调 Java ======
    usb_descriptor_iter_init(device, &iter);
    while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
        if (desc->bDescriptorType == USB_DT_CONFIG) {
            // ★ 回调 Java ------ addUsbConfiguration()
            env->CallVoidMethod(thiz, method_addUsbConfiguration,
                    config->bConfigurationValue, configName,
                    config->bmAttributes, config->bMaxPower);

        } else if (desc->bDescriptorType == USB_DT_INTERFACE) {
            // ★ 回调 Java ------ addUsbInterface()
            env->CallVoidMethod(thiz, method_addUsbInterface,
                    interface->bInterfaceNumber, interfaceName,
                    interface->bAlternateSetting,
                    interface->bInterfaceClass,
                    interface->bInterfaceSubClass,
                    interface->bInterfaceProtocol);

        } else if (desc->bDescriptorType == USB_DT_ENDPOINT) {
            // ★ 回调 Java ------ addUsbEndpoint()
            // 注意:wMaxPacketSize 需做小端字节序转换
            env->CallVoidMethod(thiz, method_addUsbEndpoint,
                    endpoint->bEndpointAddress, endpoint->bmAttributes,
                    __le16_to_cpu(endpoint->wMaxPacketSize), endpoint->bInterval);
        }
    }

    // ====== 第五步:回调 Java ------ endUsbDeviceAdded() ======
    env->CallVoidMethod(thiz, method_endUsbDeviceAdded);

fail:
    usb_device_close(device);
    checkAndClearExceptionFromCallback(env, __FUNCTION__);  // ★ 异常清除
    return 0;
}

回调时序图

复制代码
JNI (C++)                              Java (UsbHostManager)
─────────                              ─────────────────────
usb_device_added()
  │
  ├─ beginUsbDeviceAdded() ──────────→ mNewDevice = new UsbDevice(...)
  │                                      mNewConfigurations = new ArrayList<>()
  │                                      mNewInterfaces = new ArrayList<>()
  │                                      mNewEndpoints = new ArrayList<>()
  │
  ├─ addUsbConfiguration() ──────────→ mNewConfiguration.setInterfaces(...)
  │                                      mNewConfigurations.add(mNewConfiguration)
  │
  ├─ addUsbInterface() ──────────────→ mNewInterface.setEndpoints(...)
  │                                      mNewInterfaces.add(mNewInterface)
  │
  ├─ addUsbEndpoint() ───────────────→ mNewEndpoints.add(new UsbEndpoint(...))
  │
  ├─ ... (重复以上步骤,每个描述符一次回调)
  │
  └─ endUsbDeviceAdded() ────────────→ mNewDevice.setConfigurations(...)
                                         mDevices.put(deviceName, mNewDevice)
                                         getCurrentSettings().deviceAttached(mNewDevice)
                                         mUsbAlsaManager.usbDeviceAdded(mNewDevice)

3.5 JNI 层:usb_device_removed()

cpp 复制代码
static int usb_device_removed(const char *devname, void* client_data) {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    jobject thiz = (jobject)client_data;

    jstring deviceName = env->NewStringUTF(devname);
    env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceName);
    env->DeleteLocalRef(deviceName);
    checkAndClearExceptionFromCallback(env, __FUNCTION__);  // ★ 异常清除
    return 0;
}

四、UsbHostManager 分段回调方法 ------ 完整源码拆解

4.1 beginUsbDeviceAdded() ------ 设备添加开始

java 复制代码
/* Called from JNI in monitorUsbHostBus() to report new USB devices
   Returns true if successful, in which case the JNI code will continue adding
   configurations, interfaces and endpoints, and finally call endUsbDeviceAdded
   after all descriptors have been processed */
private boolean beginUsbDeviceAdded(String deviceName, int vendorID, int productID,
        int deviceClass, int deviceSubclass, int deviceProtocol,
        String manufacturerName, String productName, int version, String serialNumber) {

    if (DEBUG) {
        Slog.d(TAG, "usb:UsbHostManager.beginUsbDeviceAdded(" + deviceName + ")");
        Slog.d(TAG, "usb: nm:" + deviceName + " vnd:" + vendorID + " prd:" + productID
                + " cls:" + deviceClass + " sub:" + deviceSubclass + " proto:" + deviceProtocol);
    }

    // ====== 黑名单检查 ======
    if (isBlackListed(deviceName) ||
            isBlackListed(deviceClass, deviceSubclass, deviceProtocol)) {
        return false;  // ★ 返回 false,JNI 层停止解析此设备
    }

    synchronized (mLock) {
        // ====== 重复检查 ======
        if (mDevices.get(deviceName) != null) {
            Slog.w(TAG, "device already on mDevices list: " + deviceName);
            return false;
        }

        if (mNewDevice != null) {
            Slog.e(TAG, "mNewDevice is not null in endUsbDeviceAdded");
            return false;
        }

        // ====== 构建 USB 版本号字符串 ======
        // version 是 BCD 格式:高字节为主版本号,低字节为次版本号
        // 例如 0x0210 → "2.16"
        String versionString = Integer.toString(version >> 8) + "." + (version & 0xFF);

        // ====== 创建 UsbDevice 对象(不含 Configurations) ======
        mNewDevice = new UsbDevice(deviceName, vendorID, productID,
                deviceClass, deviceSubclass, deviceProtocol,
                manufacturerName, productName, versionString, serialNumber);

        // ====== 初始化临时容器 ======
        mNewConfigurations = new ArrayList<UsbConfiguration>();
        mNewInterfaces = new ArrayList<UsbInterface>();
        mNewEndpoints = new ArrayList<UsbEndpoint>();
    }

    return true;  // ★ 返回 true,JNI 层继续解析描述符
}

4.2 黑名单过滤机制

java 复制代码
// 方法一:基于设备名(总线路径)的黑名单
private boolean isBlackListed(String deviceName) {
    int count = mHostBlacklist.length;
    for (int i = 0; i < count; i++) {
        if (deviceName.startsWith(mHostBlacklist[i])) {
            return true;
        }
    }
    return false;
}

// 方法二:基于设备类/子类/协议的黑名单
private boolean isBlackListed(int clazz, int subClass, int protocol) {
    // ★ 黑名单 hub(USB_CLASS_HUB = 0x09)
    if (clazz == UsbConstants.USB_CLASS_HUB) return true;

    // ★ 黑名单 HID boot 设备(鼠标/键盘,由 InputFlinger 接管)
    if (clazz == UsbConstants.USB_CLASS_HID &&
            subClass == UsbConstants.USB_INTERFACE_SUBCLASS_BOOT) {
        return true;
    }

    return false;
}

黑名单配置来自 frameworks/base/core/res/res/values/config.xml 中的 config_usbHostBlacklist 数组,支持按设备路径前缀匹配,同时硬编码过滤 HUB 和 HID boot 设备。

4.3 addUsbConfiguration() ------ 添加配置

java 复制代码
private void addUsbConfiguration(int id, String name, int attributes, int maxPower) {
    // ★ 上一个 Configuration 的接口已经收集完毕,打包到 mNewConfiguration
    if (mNewConfiguration != null) {
        mNewConfiguration.setInterfaces(
                mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
        mNewInterfaces.clear();
    }

    // ★ 创建新的 UsbConfiguration
    mNewConfiguration = new UsbConfiguration(id, name, attributes, maxPower);
    mNewConfigurations.add(mNewConfiguration);
}

4.4 addUsbInterface() ------ 添加接口

java 复制代码
private void addUsbInterface(int id, String name, int altSetting,
        int Class, int subClass, int protocol) {
    // ★ 上一个 Interface 的端点已经收集完毕,打包到 mNewInterface
    if (mNewInterface != null) {
        mNewInterface.setEndpoints(
                mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
        mNewEndpoints.clear();
    }

    // ★ 创建新的 UsbInterface
    mNewInterface = new UsbInterface(id, altSetting, name, Class, subClass, protocol);
    mNewInterfaces.add(mNewInterface);
}

4.5 addUsbEndpoint() ------ 添加端点

java 复制代码
private void addUsbEndpoint(int address, int attributes, int maxPacketSize, int interval) {
    mNewEndpoints.add(new UsbEndpoint(address, attributes, maxPacketSize, interval));
}

4.6 endUsbDeviceAdded() ------ 设备添加完成,发送广播

java 复制代码
private void endUsbDeviceAdded() {
    if (DEBUG) {
        Slog.d(TAG, "usb:UsbHostManager.endUsbDeviceAdded()");
    }

    // ====== 收尾工作:将最后一个 Interface 的端点打包 ======
    if (mNewInterface != null) {
        mNewInterface.setEndpoints(
                mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
    }
    // ====== 将最后一个 Configuration 的接口打包 ======
    if (mNewConfiguration != null) {
        mNewConfiguration.setInterfaces(
                mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
    }

    synchronized (mLock) {
        if (mNewDevice != null) {
            // ====== 将 Configurations 设置到 UsbDevice ======
            mNewDevice.setConfigurations(
                    mNewConfigurations.toArray(new UsbConfiguration[mNewConfigurations.size()]));

            // ====== 加入设备列表 ======
            mDevices.put(mNewDevice.getDeviceName(), mNewDevice);
            Slog.d(TAG, "usbDeviceAdded device " + mNewDevice);

            // ====== ★ 发送广播!通过 UsbSettingsManager ======
            getCurrentSettings().deviceAttached(mNewDevice);

            // ====== 通知 USB 音频管理器 ======
            mUsbAlsaManager.usbDeviceAdded(mNewDevice);
        } else {
            Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded");
        }

        // ====== 清理临时变量 ======
        mNewDevice = null;
        mNewConfigurations = null;
        mNewInterfaces = null;
        mNewEndpoints = null;
        mNewConfiguration = null;
        mNewInterface = null;
    }
}

4.7 usbDeviceRemoved() ------ 设备移除

java 复制代码
/* Called from JNI in monitorUsbHostBus to report USB device removal */
private void usbDeviceRemoved(String deviceName) {
    synchronized (mLock) {
        // ====== 从设备列表移除 ======
        UsbDevice device = mDevices.remove(deviceName);
        Slog.d(TAG, "usbDeviceRemoved deviceName = " + deviceName + "device = " + device);

        if (device != null) {
            // ====== 通知 USB 音频管理器 ======
            mUsbAlsaManager.usbDeviceRemoved(device);
            // ====== ★ 发送移除广播!通过 UsbSettingsManager ======
            getCurrentSettings().deviceDetached(device);
        }
    }
}

关键点Slog.d()DEBUG = false 时不会输出,所以 usbDeviceRemoved() 的调用日志在 logcat 中不可见,只能通过间接证据(如应用层收到的 DETACHED 广播)来推断它被调用了。


五、UsbSettingsManager ------ 广播发送的实际执行者

源码路径frameworks/base/services/usb/java/com/android/server/usb/UsbSettingsManager.java

Android 7 中,广播发送不在 UsbHostManager 中,而是委托给 UsbSettingsManager。这种设计将设备感知和权限/广播管理解耦。

5.1 deviceAttached() ------ 发送 ATTACHED 广播

java 复制代码
public void deviceAttached(UsbDevice device) {
    // ====== 构建 Intent ======
    final Intent intent = createDeviceAttachedIntent(device);
    Slog.d(TAG, "usbDeviceAdded, sending " + intent);

    // ====== ★ 发送广播到当前用户 ======
    mUserContext.sendBroadcast(intent);

    // ====== 检查是否需要显示 MTP 通知 ======
    if (MtpNotificationManager.shouldShowNotification(mPackageManager, device)) {
        mMtpNotificationManager.showNotification(device);
    } else {
        // ====== 解析匹配的 Activity(通过 intent-filter) ======
        resolveActivity(intent, device);
    }
}

private static Intent createDeviceAttachedIntent(UsbDevice device) {
    Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
    intent.putExtra(UsbManager.EXTRA_DEVICE, device);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  // ★ 注意:是 NEW_TASK 而非 FOREGROUND
    return intent;
}

5.2 deviceDetached() ------ 发送 DETACHED 广播

java 复制代码
public void deviceDetached(UsbDevice device) {
    // ====== 清除临时权限 ======
    mDevicePermissionMap.remove(device.getDeviceName());

    // ====== ★ 构建并发送广播到所有用户 ======
    Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
    intent.putExtra(UsbManager.EXTRA_DEVICE, device);
    Slog.d(TAG, "usbDeviceRemoved, sending " + intent);
    mContext.sendBroadcastAsUser(intent, UserHandle.ALL);

    // ====== 隐藏 MTP 通知 ======
    mMtpNotificationManager.hideNotification(device.getDeviceId());
}

设计要点

  • ATTACHED 广播使用 FLAG_ACTIVITY_NEW_TASK(用于拉起 resolver activity)发送到当前用户
  • DETACHED 广播发送给所有用户(sendBroadcastAsUser(intent, UserHandle.ALL)
  • ATTACHED 时还会自动尝试解析和启动匹配的 Activity(如文件管理器)

六、UsbDevice 对象树结构

以实际日志中的 SanDisk U 盘为例:

复制代码
UsbDevice  (SanDisk 3.2Gen1)
  │ mName="/dev/bus/usb/001/006"
  │ mVendorId=0x0781, mProductId=0x55a9
  │ mClass=0x00, mSubclass=0x00, mProtocol=0x00
  │ mSerialNumber="03001911102425174343"
  │ mVersion="2.16"
  │
  └── UsbConfiguration[0] (id=1, maxPower=112mA)
        │
        ├── UsbInterface[0] (id=0, altSetting=0)
        │     │ mClass=0x08 (Mass Storage)
        │     │ mSubclass=0x06 (SCSI transparent)
        │     │ mProtocol=0x50 (Bulk-Only Transport)
        │     │
        │     ├── UsbEndpoint (address=0x81, type=BULK, IN, maxPacketSize=512)
        │     │     → 数据输入(设备→主机)
        │     │
        │     └── UsbEndpoint (address=0x02, type=BULK, OUT, maxPacketSize=512)
        │           → 数据输出(主机→设备)
        │
        └── UsbInterface[0] (id=0, altSetting=1)  ← 同一接口的备用设置
              │ mClass=0x08, mSubclass=0x06, mProtocol=0x62
              │
              ├── UsbEndpoint (address=0x82, type=BULK, IN)
              ├── UsbEndpoint (address=0x03, type=BULK, OUT)
              ├── UsbEndpoint (address=0x84, type=BULK, IN)
              └── UsbEndpoint (address=0x01, type=BULK, OUT)

这个树形结构被完整地序列化到 IntentEXTRA_DEVICE 中,通过 Binder 跨进程传递到接收广播的应用。


七、广播接收方分析

7.1 广播常量

源码路径frameworks/base/core/java/android/hardware/usb/UsbManager.java

java 复制代码
public static final String ACTION_USB_DEVICE_ATTACHED =
        "android.hardware.usb.action.USB_DEVICE_ATTACHED";

public static final String ACTION_USB_DEVICE_DETACHED =
        "android.hardware.usb.action.USB_DEVICE_DETACHED";

八、与 vold 的并行对比

现在可以清晰地看到两条并行链路的设计差异:

维度 UsbHostManager 链路 vold + MountService 链路
感知方式 JNI → libusbhost → inotify 监听 /dev/bus/usb/ netlink socket 监听 SUBSYSTEM=block uevent
关注对象 USB 设备本身(vendor/product/class) Block 设备(分区/文件系统)
核心数据 UsbDeviceUsbConfigurationUsbInterfaceUsbEndpoint DiskInfoVolumeInfo
广播 USB_DEVICE_ATTACHED / USB_DEVICE_DETACHED VOLUME_STATE_CHANGED
系统回调 无(纯广播) StorageEventListener(Binder 回调,依赖 vold NDC)
触发时机 ★ USB 硬件连接/断开瞬间 分区表识别 → 文件系统挂载完成
主要消费者 需要操作 USB 设备的 App SystemUI 、MediaScanner
典型场景 U 盘识别、USB 摄像头、USB 串口 U 盘文件读写、媒体扫描
总线复位时 ✅ 正常工作(广播正常发送) ❌ 断裂(vold 不感知总线复位)

调用链路总图

复制代码
内核 USB 驱动
    │
    ├── uevent (SUBSYSTEM=block)          uevent (SUBSYSTEM=usb)
    │       │                                    │
    │       ▼                                    ▼
    │   vold (netlink)                    libusbhost (inotify)
    │       │                                    │
    │       ▼                                    ▼
    │   MountService             JNI: usb_device_added()
    │       │                                    │
    │       ├── Binder 回调 ──→ StatusBarView    │
    │       │   (StorageEventListener)           │
    │       │                                    │
    │       └── 广播 ──→ USBdiskReceiver         │
    │            VOLUME_STATE_CHANGED            │
    │                                            │
    └──────────────── USB 广播 ◄─────────────────┘
                      USB_DEVICE_ATTACHED/DETACHED
                            │
                            ▼
                      BroadcastReceiver (应用)

九、关键源码文件索引

复制代码
frameworks/base/services/
├── java/com/android/server/SystemServer.java
│       → startBootstrapServices() 中启动 UsbService
│
├── usb/java/com/android/server/usb/
│   ├── UsbService.java
│   │       → 条件创建子模块,per-user SettingsManager
│   ├── UsbHostManager.java              ★ 本文主角
│   │       → 分段回调:begin→addConfiguration→addInterface→addEndpoint→end
│   │       → 设备列表管理、黑名单过滤
│   ├── UsbSettingsManager.java
│   │       → ★ 广播发送的实际执行者
│   │       → deviceAttached() / deviceDetached()
│   │       → 权限管理、默认应用解析
│   ├── UsbDeviceManager.java
│   │       → 设备模式(手机作为 U 盘/充电)
│   ├── UsbPortManager.java
│   │       → Type-C 端口管理
│   └── UsbAlsaManager.java
│           → USB 音频设备
│
├── core/jni/com_android_server_UsbHostManager.cpp
│       → ★ JNI 桥接层
│       → usb_host_init() + usb_host_run()
│       → 逐个描述符解析并回调 Java

frameworks/base/core/java/android/hardware/usb/
├── UsbManager.java
│       → ACTION_USB_DEVICE_ATTACHED / DETACHED 常量定义
├── UsbDevice.java
│       → UsbDevice Parcelable 对象(含 setConfigurations)
├── UsbConfiguration.java
│       → UsbConfiguration(含 setInterfaces)
├── UsbInterface.java
│       → UsbInterface(含 setEndpoints)
└── UsbEndpoint.java
        → UsbEndpoint(地址、属性、最大包大小、间隔)

system/core/libusbhost/
├── usbhost.c
│       → usb_host_init() / usb_host_run() 实现
│       → inotify + 轮询 /dev/bus/usb/ 目录
└── include/usbhost/usbhost.h
        → libusbhost API 声明

十、总结

Android 7 的 UsbHostManager 架构核心要点:

维度 实现方式
JNI 回调模式 分段回调(6个方法:begin→addConfiguration→addInterface→addEndpoint→end→remove)
描述符解析 JNI 层(C 代码),使用 libusbhost 库
广播发送 委托给 UsbSettingsManager
广播 Flag ATTACHED: FLAG_ACTIVITY_NEW_TASK, DETACHED: 无特殊 flag
子模块创建 条件创建(FEATURE_USB_HOST + /sys/class/android_usb 检查)
SettingsManager per-user(SparseArray)
DEBUG 开关 false(默认关闭,调试日志不可见)
底层库 libusbhost(usb_host_init + usb_host_run)

核心工作流程:

  1. SystemServer 启动 UsbService,条件创建 UsbHostManager
  2. systemReady() 创建独立线程,进入 JNI 层 monitorUsbHostBus()
  3. libusbhost 通过 inotify 监听 /dev/bus/usb/,发现设备后回调 usb_device_added()
  4. JNI 分段回调beginUsbDeviceAdded()addUsbConfiguration()addUsbInterface()addUsbEndpoint()endUsbDeviceAdded()
  5. endUsbDeviceAdded() 中通过 UsbSettingsManager.deviceAttached() 发送 ACTION_USB_DEVICE_ATTACHED 广播
  6. 应用层通过 BroadcastReceiver 接收广播,感知 USB 设备插入

下一篇我们将深入 MountService,分析 vold 如何将 block 设备事件转化为 VolumeInfo 状态变化。