系列目录 :第一篇:全景图与调用链路概览 | 第二篇:内核层---USB驱动与uevent | 第三篇:Native层---vold与NetlinkManager | 第四篇:Framework层(上)---UsbHostManager | 第五篇:Framework层(下)---StorageManagerService | 第六篇:广播分发与SystemUI响应 | 第七篇:应用层---MediaScanner与SAF | 第八篇:实战调试与案例分析
一、为什么写这个系列
在 Android ROM 定制和系统开发中,U盘/OTG 问题是排查频率最高的场景之一:
- 插入 U 盘没反应,logcat 一片寂静?
- 能识别设备但无法挂载?
- 挂载成功,第三方应用却读不到文件?
- 多分区 U 盘只能识别第一个?
要精准定位这些问题,靠百度出来的零散博客是远远不够的------你必须理解从物理插入到 Toast 弹出的整条调用链。
本系列将以 AOSP 12(Android 12) 源码为基础,从 Kernel → Native → Framework → Application 逐层拆解,最终让你具备"看日志就能定位问题"的能力。
二、分层架构总览
Android 系统拿到一个 U 盘,需要经过 四层协作:
┌──────────────────────────────────────────────────────────┐
│ Application Layer (应用层) │
│ ┌──────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Settings │ │ DocumentsUI │ │ 第三方文件管理器 │ │
│ └────┬─────┘ └──────┬───────┘ └────────┬─────────┘ │
│ │ │ │ │
│ │ MediaStore / SAF / StorageManager API │
├───────┼───────────────┼───────────────────┼──────────────┤
│ Framework Layer (Java框架层) │
│ ┌────┴────────────────┴───────────────────┴──────────┐ │
│ │ StorageManagerService │ │
│ │ ┌──────────────────┐ ┌──────────────────────┐ │ │
│ │ │ UsbHostManager │ │ MountService(Vold) │ │ │
│ │ └────────┬─────────┘ └──────────┬───────────┘ │ │
│ └───────────┼───────────────────────┼────────────────┘ │
│ │ JNI │ AIDL(Binder) │
├──────────────┼───────────────────────┼────────────────────┤
│ Native Layer (C++原生层) │
│ ┌───────────┴───────────┐ ┌───────┴──────────────────┐ │
│ │ com_android_server_ │ │ vold (Volume Daemon) │ │
│ │ UsbHostManager.cpp │ │ ┌───────────────────┐ │ │
│ │ │ │ │ NetlinkManager │ │ │
│ │ monitorUsbHostBus() │ │ │ VolumeManager │ │ │
│ └───────────┬───────────┘ │ │ Disk / Volume │ │ │
│ │ │ └───────────────────┘ │ │
│ │ └───────┬──────────────────┘ │
│ │ sysfs/uevent │ netlink socket │
├──────────────┼────────────────────┼──────────────────────┤
│ Kernel Layer (内核层) │
│ ┌───────────┴────────────────────┴──────────────────┐ │
│ │ USB Core → USB Storage Driver → Block Layer │ │
│ │ ↓ uevent (KOBJ_ADD) │ │
│ └───────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
各层核心组件速查表
| 层级 | 核心组件 | 源码路径(基于 AOSP 12) | 职责 |
|---|---|---|---|
| Kernel | USB Core / usb-storage | drivers/usb/core/hub.c |
硬件枚举、驱动绑定、uevent 生成 |
| Native | vold + NetlinkManager | system/vold/ |
监听 uevent、分区解析、文件系统挂载 |
| Native | UsbHostManager JNI | frameworks/base/services/.../jni/ |
USB 设备信息采集、JNI 回调 |
| Framework | UsbHostManager | frameworks/.../usb/UsbHostManager.java |
UsbDevice 构建、USB_DEVICE_ATTACHED 广播 |
| Framework | UsbService | frameworks/.../usb/UsbService.java |
USB 服务生命周期管理 |
| Framework | StorageManagerService | frameworks/.../StorageManagerService.java |
存储卷管理、与 vold Binder 通信 |
| Application | MediaProvider | packages/providers/MediaProvider/ |
媒体文件扫描、MediaStore 写入 |
| Application | ExternalStorageProvider | packages/providers/.../ExternalStorageProvider/ |
SAF 框架暴露 U 盘文件 |
三、完整时序图:从插入到可用
下面用一张 ASCII 时序图 展示整个链路。建议你把它收藏起来,后续每一篇都会频繁引用。
时间 ──────────────────────────────────────────────────────────────►
物理层 Kernel vold UsbHostManager StorageMgr App层
────── ────── ──── ────────────── ────────── ────
U盘插入
│
├──[1]──► VBUS检测
│ hub_events()
│ hub_port_connect_change()
│
├──[2]──► USB枚举
│ (获取设备/配置/接口
│ 描述符, 绑定驱动)
│
├──[3]──► usb-storage驱动绑定
│ /dev/sda 节点创建
│
├──[4]──► kobject_uevent_env(KOBJ_ADD)
│ ──netlink──►
│ [5] NetlinkHandler::onEvent()
│ 解析 DEVPATH/SUBSYSTEM
│
│ [6] VolumeManager::handleBlockEvent()
│ Disk::create()
│ Disk::readPartitions()
│ (MBR/GPT解析)
│
│ [7] PublicVolume::create()
│ 检测文件系统类型
│
│ ──binder──► [8] onDiskCreated()
│ onVolumeCreated()
│
│ [9] StorageManagerService
│ mountVolume()
│ ──binder──►
│ [10] VolumeManager::mountVolume()
│ mount(2) 系统调用
│ (挂载到 /mnt/media_rw/XXXX)
│
│ ◄──binder──
│ [11] 发送广播
│ ACTION_MEDIA_MOUNTED
│
│ ┌──────────────────────────────────────────┐
│ │ ★ 与此同时,另一条并行的 USB 感知链路: │
│ │ │
│ │ vold 处理 block 事件时 │
│ │ UsbHostManager 也同时在工作 │
│ └──────────┬───────────────────────────────┘
│ │
│ │ JNI: monitorUsbHostBus()
│ │ 读取/sys/class/android_usb/...
│ │
│ ▼
│ [12] UsbHostManager.usbDeviceAttached()
│ 创建 UsbDevice 对象树
│ (接口、端点信息)
│
│ [13] 发送广播
│ ACTION_USB_DEVICE_ATTACHED
│ ──► 应用接收
│ 注册USB设备
│
│ ──► MediaScanner
│ MediaScannerReceiver
│ 扫描挂载点
│ MediaStore写入
│
│ ──► SystemUI
│ 通知栏"U盘已插入"
│ Settings刷新
│
▼
U盘可用
关键观察 :USB 设备感知(UsbHostManager)和存储挂载(StorageManagerService)是 两条并行的链路。前者告诉你"插了个什么设备",后者告诉你"这个设备的存储可以用了"。
四、两大并行链路的详细拆解
为了让你建立起清晰的思维模型,我把整条链路拆成 两条主线 来理解:
链路 A:USB 设备感知链(偏"识别")
Kernel USB枚举
→ uevent(KOBJ_ADD到android_usb子系统)
→ JNI monitorUsbHostBus() 监听/sys
→ UsbHostManager.usbDeviceAttached()
→ 构建 UsbDevice 对象树
→ 发送 ACTION_USB_DEVICE_ATTACHED 广播
→ App 通过 BroadcastReceiver 接收
核心数据结构:
java
// UsbDevice:代表一个物理USB设备
public class UsbDevice {
String mName; // 设备名称,如 "/dev/bus/usb/001/002"
int mVendorId; // 厂商ID,如 0x0781 (SanDisk)
int mProductId; // 产品ID
int mClass; // 设备类,如 0x08 = Mass Storage
int mSubclass;
int mProtocol;
String mManufacturerName; // 制造商字符串
String mProductName; // 产品名称字符串
String mSerialNumber; // 序列号
Parcelable[] mConfigurations; // UsbConfiguration数组
UsbInterface[] mInterfaces; // UsbInterface数组
}
// UsbInterface:设备的一个功能接口
public class UsbInterface {
int mId;
int mClass; // 如 0x08 = Mass Storage
UsbEndpoint[] mEndpoints; // 通信端点
}
// UsbEndpoint:数据传输端点
public class UsbEndpoint {
int mAddress; // 端点地址(含方向)
int mType; // CONTROL/BULK/INTERRUPT/ISOCHRONOUS
int mMaxPacketSize; // 最大包大小
}
链路 B:存储挂载链(偏"可用")
Kernel block设备创建(/dev/sda)
→ uevent(KOBJ_ADD, SUBSYSTEM=block)
→ vold NetlinkHandler 捕获
→ Disk::create() + readPartitions()
→ PublicVolume::create()
→ StorageManagerService.mountVolume()
→ vold mount(2) 到 /mnt/media_rw/XXXX
→ 发送 ACTION_MEDIA_MOUNTED 广播
→ MediaScanner 扫描
→ SAF 暴露文件系统
核心数据结构:
java
// DiskInfo:代表一个物理磁盘(在Java层)
public class DiskInfo {
public String id; // "disk:179,0"
public int flags;
public long size;
public String label;
public int volumeCount;
}
// VolumeInfo:代表磁盘上的一个卷/分区
public class VolumeInfo {
public String id; // "public:179,1"
public int type; // TYPE_PUBLIC / TYPE_PRIVATE / TYPE_EMULATED
public String diskId;
public String partGuid;
public int mountFlags;
public int state; // STATE_UNMOUNTED → STATE_MOUNTED
public String fsType; // "vfat", "exfat", "ntfs", "ext4"...
public String path; // 挂载点: "/mnt/media_rw/XXXX"
}
五、链路中的关键广播汇总
这条链路中,系统会依次发出多个广播。理解它们的顺序和携带的数据,是排查问题的关键。
| 广播 Action | 发送者 | 触发时机 | 携带关键Extra | 备注 |
|---|---|---|---|---|
android.hardware.usb.action.USB_DEVICE_ATTACHED |
UsbHostManager | USB设备枚举完成 | EXTRA_DEVICE (UsbDevice) |
带 FLAG_RECEIVER_FOREGROUND |
android.hardware.usb.action.USB_DEVICE_DETACHED |
UsbHostManager | USB设备拔出 | EXTRA_DEVICE (UsbDevice) |
带 FLAG_RECEIVER_FOREGROUND |
android.intent.action.MEDIA_UNMOUNTED |
StorageManagerService | 卷开始卸载 | EXTRA_VOLUME_STATE |
|
android.intent.action.MEDIA_CHECKING |
StorageManagerService | 卷正在检查 | EXTRA_VOLUME_STATE |
|
android.intent.action.MEDIA_MOUNTED |
StorageManagerService | 卷挂载完成 | EXTRA_VOLUME_STATE, path |
最重要 |
android.intent.action.MEDIA_EJECT |
StorageManagerService | 用户操作弹出 | EXTRA_VOLUME_STATE |
|
android.intent.action.MEDIA_BAD_REMOVAL |
StorageManagerService | 未安全弹出就拔出 | EXTRA_VOLUME_STATE |
|
android.intent.action.MEDIA_SCANNER_STARTED |
MediaScannerReceiver | 开始扫描文件 | volume path | |
android.intent.action.MEDIA_SCANNER_FINISHED |
MediaScannerReceiver | 扫描完成 | volume path | 之后文件才可在MediaStore查到 |
实战提示 :调试时在 logcat 中
grep这些 action 字符串,可以快速判断卡在哪一步。如果MEDIA_MOUNTED没出现,问题在存储挂载链;如果出现了但MEDIA_SCANNER_FINISHED没出现,问题在媒体扫描。
六、源码阅读准备
在开始深入分析之前,需要准备好以下环境:
6.1 源码下载
bash
# 下载 AOSP 12 源码(建议只下载必要分支)
repo init -u https://android.googlesource.com/platform/manifest -b android-12.0.0_r1
repo sync -j8
# 如果只需要本地索引阅读,推荐使用
# Android Code Search: https://cs.android.com
# 或本地镜像: https://android.googlesource.com
6.2 关键源码目录
frameworks/base/services/
├── usb/java/com/android/server/usb/
│ ├── UsbService.java # USB服务入口
│ ├── UsbHostManager.java # ★ Host模式核心
│ ├── UsbDeviceManager.java # Device模式(手机当U盘)
│ ├── UsbAlsaManager.java # USB音频管理
│ └── UsbSettingsManager.java # USB设置管理
│
├── core/java/com/android/server/
│ └── StorageManagerService.java # ★ 存储挂载核心
│
system/vold/
├── NetlinkManager.cpp # ★ uevent监听
├── NetlinkHandler.cpp # uevent处理
├── VolumeManager.cpp # ★ 卷管理
├── model/
│ ├── Disk.cpp # 磁盘模型
│ ├── PublicVolume.cpp # ★ 公共卷(U盘)
│ ├── PrivateVolume.cpp # 私有卷(adoptable storage)
│ └── EmulatedVolume.cpp # 模拟卷(内部存储)
│
frameworks/native/libs/binder/
└── ... # Binder IPC 通信
七、小结
本文作为系列开篇,为你建立了 U盘插拔全链路的知识框架:
- 四层协作模型:Kernel → Native(vold/JNI) → Framework(UsbHostManager/StorageManagerService) → Application
- 两条并行主线:USB 设备识别链 + 存储卷挂载链
- 关键广播序列 :从
USB_DEVICE_ATTACHED到MEDIA_MOUNTED到MEDIA_SCANNER_FINISHED - 核心数据结构:UsbDevice / UsbInterface / UsbEndpoint 和 Disk / Volume
理解了这些,你已经可以对 80% 的 U 盘问题做出初步判断:
- 日志中没有
USB_DEVICE_ATTACHED?→ 硬件/内核层出问题,第二篇见 - 有 `USB_DEV