系列目录 :第一篇:全景图与调用链路概览 | 第二篇:内核层---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/):
- 打开
/dev/bus/usb/目录并枚举所有现有设备 - 使用 inotify 监听
/dev/bus/usb/目录的IN_CREATE/IN_DELETE事件 - 发现新设备时调用
usb_device_added()回调 - 发现设备移除时调用
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)
这个树形结构被完整地序列化到 Intent 的 EXTRA_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 设备(分区/文件系统) |
| 核心数据 | UsbDevice → UsbConfiguration → UsbInterface → UsbEndpoint |
DiskInfo → VolumeInfo |
| 广播 | 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) |
核心工作流程:
- SystemServer 启动
UsbService,条件创建UsbHostManager - systemReady() 创建独立线程,进入 JNI 层
monitorUsbHostBus() - libusbhost 通过 inotify 监听
/dev/bus/usb/,发现设备后回调usb_device_added() - JNI 分段回调 :
beginUsbDeviceAdded()→addUsbConfiguration()→addUsbInterface()→addUsbEndpoint()→endUsbDeviceAdded() - endUsbDeviceAdded() 中通过
UsbSettingsManager.deviceAttached()发送ACTION_USB_DEVICE_ATTACHED广播 - 应用层通过
BroadcastReceiver接收广播,感知 USB 设备插入
下一篇我们将深入 MountService,分析 vold 如何将 block 设备事件转化为 VolumeInfo 状态变化。