Android7 U盘插拔链路源码全解析(三)Native层Vold与NetlinkManage
系列目录:第一篇:全景图与调用链路概览 | 第二篇:内核层—USB驱动与uevent | 第三篇:Native层—vold与NetlinkManager | 第四篇:Framework层(上)—UsbHostManager | 第五篇:Framework层(下)—MountService | 第六篇:广播分发与SystemUI响应 | 第七篇:应用层—MediaScanner与SAF | 第八篇:实战调试与案例分析
一、引言
上一篇文章我们分析了 Linux 内核如何完成 USB 枚举、绑定 usb-storage 驱动、创建 /dev/sda 节点,最后通过 netlink 发送 uevent。
本篇聚焦 vold(Volume Daemon)——用户态第一个感知 U 盘插入的系统进程。它的任务链是:
接收 uevent → 解析设备信息 → 创建 Disk 对象
→ 读取分区表 → 创建 PublicVolume 对象
→ 通知 Java 层 → 响应挂载请求 → 执行 mount(2)
vold 是整个存储子系统的 Native 中枢,稳定性和健壮性直接影响用户是否能正常使用 U 盘。
二、vold 架构全景
2.1 进程模型
vold 是一个 C++ 守护进程,由 init 启动:
# system/vold/vold.rc
service vold /system/bin/vold \
--blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \
--fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0
class core
socket vold stream 0660 root mount
socket cryptd stream 0660 root mount
ioprio be 2
writepid /dev/cpuset/foreground/tasks
2.2 五大核心模块
┌─────────────────────────────────────────────────────────────┐
│ vold (Volume Daemon) │
│ │
│ ┌─────────────────┐ ┌──────────────────────────────┐ │
│ │ NetlinkManager │ │ VolumeManager │ │
│ │ │ │ │ │
│ │ - 创建 netlink │ │ - handleBlockEvent() │ │
│ │ socket │ │ - mountVolume() │ │
│ │ - 接收 uevent │───►│ - unmountVolume() │ │
│ │ - 分发给 Handler │ │ - formatVolume() │ │
│ └─────────────────┘ │ - 管理 Disk/Volume 集合 │ │
│ └──────────────┬───────────────┘ │
│ │ │
│ ┌──────────────────────┐ ┌───────────┴───────────────┐ │
│ │ CommandListener │ │ Model Objects │ │
│ │ │ │ │ │
│ │ - 监听 /dev/socket/ │ │ Disk │ │
│ │ vold socket │ │ ├── PublicVolume │ │
│ │ - 处理来自 Java 层的 │ │ ├── PrivateVolume │ │
│ │ NDC 命令 │ │ └── EmulatedVolume │ │
│ └──────────────────────┘ └────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Utils Layer │ │
│ │ - blkid / fsck / mkfs / mount / sdcard(FUSE) │ │
│ │ - SELinux context 切换 │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
| 模块 | 功能 |
|---|---|
| NetlinkManager | 监听内核 netlink socket,将 uevent 分发给 NetlinkHandler |
| NetlinkHandler | 解析 uevent 文本,按 SUBSYSTEM 路由到 VolumeManager |
| VolumeManager | 创建和管理 Disk/Volume 对象,响应挂载/卸载等操作 |
| CommandListener | 通过 socket 监听来自 Java 层(MountService)的 NDC 命令 |
| Model (Disk/Volume) | 抽象物理磁盘和逻辑卷,封装分区表解析、文件系统检测等 |
三、vold 启动全流程
3.1 main() 入口
源码路径:system/vold/main.cpp
int main(int argc, char** argv) {
// 1. 解析命令行参数(--blkid_context, --fsck_context 等 SELinux context)
parse_args(argc, argv);
// 2. 初始化 SELinux 文件上下文
sehandle = selinux_android_file_context_handle();
// 3. 创建 VolumeManager 单例
VolumeManager* vm = VolumeManager::Instance();
// 4. 创建 NetlinkManager 单例
NetlinkManager* nm = NetlinkManager::Instance();
// 5. 创建两个 SocketListener:
// CommandListener —— 处理 vold socket 的 NDC 命令
// CryptCommandListener —— 处理 cryptd socket 的加密命令
CommandListener* cl = new CommandListener();
CryptCommandListener* ccl = new CryptCommandListener();
// 6. 启动 VolumeManager(创建内部 emulated volume)
vm->start();
// 7. 读取 fstab 配置,注册 DiskSource
process_config(vm, &has_adoptable);
// 8. 启动 NetlinkManager(创建 netlink socket 开始监听)
nm->start();
// 9. ★ 冷插拔:遍历 /sys/block,写入 "add\n" 触发已有设备的 uevent
coldboot("/sys/block");
// 10. 启动 SocketListener,开始响应 NDC 命令
cl->startListener();
ccl->startListener();
// 11. 进入事件循环(实际工作由 FrameworkListener 线程处理)
while (true) {
sleep(1000);
}
}
3.2 NetlinkManager::start() —— 建立 netlink 通道
源码路径:system/vold/NetlinkManager.cpp
int NetlinkManager::start() {
struct sockaddr_nl nladdr;
int sz = 64 * 1024; // 接收缓冲区 64KB
int on = 1;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = getpid(); // 绑定当前进程 PID
nladdr.nl_groups = 0xffffffff; // 监听所有 multicast group
// ★ 创建 netlink socket
if ((mSock = socket(PF_NETLINK,
SOCK_DGRAM | SOCK_CLOEXEC,
NETLINK_KOBJECT_UEVENT)) < 0) {
SLOGE("Unable to create uevent socket: %s", strerror(errno));
return -1;
}
// 设置接收缓冲区大小
if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
SLOGE("Unable to set uevent socket SO_RCVBUFFORCE option: %s", strerror(errno));
goto out;
}
// ★ 设置接收发送进程凭据(用于安全检查)
if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));
goto out;
}
// 绑定到本进程
if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
SLOGE("Unable to bind uevent socket: %s", strerror(errno));
goto out;
}
// ★ 创建 NetlinkHandler 并开始监听
mHandler = new NetlinkHandler(mSock);
if (mHandler->start()) {
SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
goto out;
}
return 0;
out:
close(mSock);
return -1;
}
三个关键参数解读:
| 参数 | 值 | 含义 |
|---|---|---|
socket() 首个参数 |
PF_NETLINK |
协议族 |
| 第三个参数 | NETLINK_KOBJECT_UEVENT |
netlink 协议类型,专用于内核对象事件 |
nl_groups |
0xffffffff |
监听所有 multicast group,不遗漏任何子系统的 uevent |
四、NetlinkHandler::onEvent() —— 路由到 VolumeManager
源码路径:system/vold/NetlinkHandler.cpp
NetlinkHandler::NetlinkHandler(int listenerSocket) :
NetlinkListener(listenerSocket) {
}
void NetlinkHandler::onEvent(NetlinkEvent *evt) {
VolumeManager *vm = VolumeManager::Instance();
const char *subsys = evt->getSubsystem();
if (!subsys) {
SLOGW("No subsystem found in netlink event");
return;
}
// ★ 只处理 block 子系统的 uevent
if (!strcmp(subsys, "block")) {
vm->handleBlockEvent(evt);
}
}
注意:vold 只关心
SUBSYSTEM=block的 uevent。USB 设备的感知(SUBSYSTEM=usb)由 UsbHostManager 的 JNI 层单独处理。
4.1 从内核 uevent 到 handleBlockEvent 的完整回调链路
onEvent() 并非由 main 线程直接调用,而是经过一个独立的监听线程逐级回调:
内核 uevent
│
▼
NetlinkManager::start() main.cpp
socket(PF_NETLINK, ..., NETLINK_KOBJECT_UEVENT)
bind()
mHandler = new NetlinkHandler(mSock)
mHandler->start()
│
▼
NetlinkHandler::start() NetlinkHandler.cpp:37
return this->startListener() ← 继承自 NetlinkListener
│
▼
SocketListener::startListener() SocketListener.cpp:72
mClients->push_back(new SocketClient(mSock))
pthread_create(&mThread, NULL, ★ 创建独立线程
threadStart, this)
│ ─── 新线程 ───
▼
SocketListener::runListener() SocketListener.cpp:152
while (1) {
select(max+1, &read_fds, ...) ← 阻塞等待内核数据
onDataAvailable(c) ← 数据到达后回调
}
│
▼
NetlinkListener::onDataAvailable() NetlinkListener.cpp:44
count = uevent_kernel_recv(socket, ← 从 netlink socket 读取
mBuffer, sizeof(mBuffer), ...)
NetlinkEvent *evt = new NetlinkEvent()
evt->decode(mBuffer, count, mFormat) ← 解析 uevent 文本
onEvent(evt) ★ 调用虚函数
delete evt
│
▼
NetlinkHandler::onEvent() NetlinkHandler.cpp:45
subsys = evt->getSubsystem()
if (!strcmp(subsys, "block")) {
vm->handleBlockEvent(evt); ★ 最终回调到 VolumeManager
}
四个关键设计要点:
-
独立线程模型:
SocketListener::startListener()内部通过pthread_create()创建一个专用线程(SocketListener.cpp:103),专门用select()轮询 netlink socket。main 线程继续执行后续的coldboot()和CommandListener启动,两者互不阻塞。 -
继承链:
NetlinkHandler → NetlinkListener → SocketListener。NetlinkHandler构造函数将 netlink socket fd 传给SocketListener(socket, false),第二个参数false表示该 socket 不需要accept()(netlink socket 是无需连接的),直接作为客户端加入mClients列表。 -
线程安全:
onDataAvailable()→onEvent()→handleBlockEvent()全部在监听线程中执行。VolumeManager::handleBlockEvent()内部的std::lock_guard<std::mutex> lock(mLock)保障了与 NDC 命令处理线程之间的互斥。 -
数据流:
uevent_kernel_recv()从 netlink socket 读取原始二进制数据,NetlinkEvent::decode()将其解析为键值对形式(如ACTION=add,DEVTYPE=disk,SUBSYSTEM=block等),之后onEvent()即可通过getSubsystem()、findParam()等方法提取字段。
五、VolumeManager::handleBlockEvent() —— 设备生命周期管理
源码路径:system/vold/VolumeManager.cpp
void VolumeManager::handleBlockEvent(NetlinkEvent* evt) {
std::lock_guard<std::mutex> lock(mLock);
std::string eventPath(evt->findParam("DEVPATH")?
evt->findParam("DEVPATH"):"");
std::string devType(evt->findParam("DEVTYPE")?
evt->findParam("DEVTYPE"):"");
// ★ 只处理 DEVTYPE=disk 的 uevent,不处理 partition 事件
if (devType != "disk") return;
int major = atoi(evt->findParam("MAJOR"));
int minor = atoi(evt->findParam("MINOR"));
dev_t device = makedev(major, minor);
switch (evt->getAction()) {
case NetlinkEvent::Action::kAdd: {
for (auto source : mDiskSources) {
if (source->matches(eventPath)) {
// 根据主设备号判断是 SD 卡还是 USB
int flags = source->getFlags();
if (major == kMajorBlockMmc) {
flags |= android::vold::Disk::Flags::kSd;
} else {
flags |= android::vold::Disk::Flags::kUsb;
}
// ★ 创建 Disk 对象并调用 create() 完成初始化
auto disk = new android::vold::Disk(
eventPath, device, source->getNickname(), flags);
disk->create(); // 内部: readMetadata() → readPartitions()
mDisks.push_back( // mDisks 是 std::list
std::shared_ptr<android::vold::Disk>(disk));
break;
}
}
break;
}
case NetlinkEvent::Action::kChange:
LOG(DEBUG) << "Disk at " << major << ":" << minor << " changed";
for (auto disk : mDisks) {
if (disk->getDevice() == device) {
disk->readMetadata();
disk->readPartitions();
}
}
break;
case NetlinkEvent::Action::kRemove:
auto i = mDisks.begin();
while (i != mDisks.end()) {
if ((*i)->getDevice() == device) {
(*i)->destroy();
i = mDisks.erase(i);
} else {
++i;
}
}
break;
}
}
5.1 Disk 创建流程
disk->create() 是 Disk 对象初始化的入口,内部依次调用:
源码路径:system/vold/Disk.cpp
status_t Disk::create() {
CHECK(!mCreated);
mCreated = true;
// 1. 通知 Java 层 DiskCreated(NDC 回调 {640 disk:8,0 8})
notifyEvent(ResponseCode::DiskCreated,
StringPrintf("%d", mFlags));
// 2. 读取元数据:ioctl 获取磁盘大小,sysfs 获取厂商标签
readMetadata();
// 3. ★ 读取分区表:调用 sgdisk --android-dump
readPartitions();
return OK;
}
关键点:partition 类型的 uevent 不会触发 handleBlockEvent 的处理。所有分区的发现和 Volume 创建都在 Disk::readPartitions() 内部完成,通过 sgdisk 工具扫描。
注意:vold 不处理
DEVTYPE=partition的 uevent,因为 Linux 内核在 disk uevent 之后还会为每个分区发送独立的DEVTYPE=partition的 uevent。vold 通过sgdisk扫描分区表来主动发现所有分区,因此主动忽略这些重复的 partition uevent,避免重复创建。
六、Disk 与分区表解析
6.1 Disk::readPartitions()
源码路径:system/vold/Disk.cpp
vold 3.0 使用 sgdisk 工具读取分区表,而不是手动解析 MBR/GPT 二进制数据:
status_t Disk::readPartitions() {
int8_t maxMinors = getMaxMinors();
if (maxMinors < 0) {
return -ENOTSUP;
}
destroyAllVolumes();
// ★ 调用 sgdisk --android-dump 读取分区表
std::vector<std::string> cmd;
cmd.push_back(kSgdiskPath); // "/system/bin/sgdisk"
cmd.push_back("--android-dump");
cmd.push_back(mDevPath); // e.g. "/dev/block/vold/disk:8,0"
std::vector<std::string> output;
status_t res = ForkExecvp(cmd, output);
if (res != OK) {
LOG(WARNING) << "sgdisk failed to scan " << mDevPath;
notifyEvent(ResponseCode::DiskScanned);
return res;
}
Table table = Table::kUnknown;
bool foundParts = false;
for (auto line : output) {
char* cline = (char*) line.c_str();
char* token = strtok(cline, " \t\n");
if (token == nullptr) continue;
if (!strcmp(token, "DISK")) {
const char* type = strtok(nullptr, " \t\n");
if (!strcmp(type, "mbr")) table = Table::kMbr;
else if (!strcmp(type, "gpt")) table = Table::kGpt;
} else if (!strcmp(token, "PART")) {
foundParts = true;
int i = strtol(strtok(nullptr, " \t\n"), nullptr, 10);
if (i <= 0 || i > maxMinors) continue;
dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);
if (table == Table::kMbr) {
const char* type = strtok(nullptr, " \t\n");
switch (strtol(type, nullptr, 16)) {
case 0x06: // FAT16
case 0x0b: case 0x0c: // W95 FAT32 (LBA)
case 0x0e: // W95 FAT16 (LBA)
createPublicVolume(partDevice);
break;
}
} else if (table == Table::kGpt) {
const char* typeGuid = strtok(nullptr, " \t\n");
const char* partGuid = strtok(nullptr, " \t\n");
if (!strcasecmp(typeGuid, kGptBasicData)) {
createPublicVolume(partDevice);
} else if (!strcasecmp(typeGuid, kGptAndroidExpand)) {
createPrivateVolume(partDevice, partGuid);
}
}
}
}
// ★ 兜底:无法识别分区表时,尝试将整个设备当作分区挂载
if (table == Table::kUnknown || !foundParts) {
std::string fsType, unused;
if (ReadMetadataUntrusted(mDevPath, fsType, unused, unused) == OK) {
createPublicVolume(mDevice);
}
}
notifyEvent(ResponseCode::DiskScanned);
return OK;
}
6.2 分区类型决策
sgdisk 输出 DISK mbr 或 DISK gpt 标记表类型后,逐行解析 PART 行。
对 MBR 按分区类型字节筛选,对 GPT 按分区类型 GUID 筛选:
| 分区表 | 类型标识 | 创建 Volume 类型 |
|---|---|---|
| MBR | 0x06 (FAT16), 0x0b/0x0c (FAT32 LBA), 0x0e (FAT16 LBA) |
PublicVolume |
| GPT | EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 (Basic data) |
PublicVolume |
| GPT | 193D1EA4-B3CA-11E4-B075-10604B889DCF (Android expand) |
PrivateVolume |
| 未知/无分区 | 兜底尝试 ReadMetadataUntrusted | PublicVolume (整盘) |
七、PublicVolume 挂载全流程
7.1 PublicVolume::doMount()
源码路径:system/vold/PublicVolume.cpp
status_t PublicVolume::doMount() {
// 1. ★ 读取文件系统元数据(blkid)
readMetadata();
// → 调用 blkid 获取 TYPE, UUID, LABEL
if (mFsType != "vfat") {
LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;
return -EIO;
}
// 2. ★ 文件系统检查(fsck_msdos)
if (vfat::Check(mDevPath)) {
LOG(ERROR) << getId() << " failed filesystem check";
return -EIO;
}
// 3. 确定挂载名
std::string stableName = getId();
if (!mFsUuid.empty()) {
if (getMountFlags() & MountFlags::kVisible) {
stableName = "sdcard1";
} else {
stableName = "Udisk";
}
}
// 4. 设置路径
mRawPath = StringPrintf("/mnt/media_rw/%s", stableName.c_str());
mFuseDefault = StringPrintf("/mnt/runtime/default/%s", stableName.c_str());
mFuseRead = StringPrintf("/mnt/runtime/read/%s", stableName.c_str());
mFuseWrite = StringPrintf("/mnt/runtime/write/%s", stableName.c_str());
// 5. ★ 执行 mount(2) 系统调用
if (vfat::Mount(mDevPath, mRawPath, false, false, false,
AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) {
PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
return -EIO;
}
// 6. ★ 启动 FUSE(sdcard 守护进程)进行权限管理
if (!(mFusePid = fork())) {
if (execl(kFusePath, kFusePath,
"-u", "1023", // AID_MEDIA_RW
"-g", "1023", // AID_MEDIA_RW
"-U", std::to_string(getMountUserId()).c_str(),
mRawPath.c_str(),
stableName.c_str(),
NULL)) {
PLOG(ERROR) << "Failed to exec";
}
_exit(1);
}
// 等待 FUSE 就绪
while (before == GetDevice(mFuseWrite)) {
usleep(50000); // 50ms
}
return OK;
}
7.2 挂载点命名规则
从实际源码可以看到,Android 7 (Nougat, Vold 3.0) 中 U 盘的挂载命名规则为:
| 场景 | stableName | 挂载路径 |
|---|---|---|
| U 盘(非主存储) | Udisk |
/mnt/media_rw/Udisk |
| SD 卡(主存储) | sdcard1 |
/mnt/media_rw/sdcard1 |
八、NDC 通信协议
8.1 协议格式
Java 层(MountService)与 vold 之间的通信使用 NDC(NativeDaemonConnector) 协议,基于 Unix Domain Socket:
命令格式: <code> <command> [args...]
→ 例如: "10 volume mount public:8,1 0 0"
响应格式: {<code> <key> <value>...}
→ 例如: "{640 disk:8,0 8}"
→ 例如: "{200 10 Command succeeded}"
8.2 实际日志中的 NDC 通信
从实际日志可以看到完整的 NDC 交互:
06-10 11:13:02.728 D VoldConnector: RCV <- {640 disk:8,0 8}
↑ disk 创建,flags=8
06-10 11:13:02.728 D VoldConnector: RCV <- {641 disk:8,0 123009761280}
↑ disk 大小 = 123009761280 字节
06-10 11:13:02.728 D VoldConnector: RCV <- {642 disk:8,0 USB}
↑ disk 标签 = "USB"
06-10 11:13:02.760 D VoldConnector: RCV <- {650 public:8,1 0 "disk:8,0" ""}
↑ volume 创建,type=0(PUBLIC)
06-10 11:13:02.761 D VoldConnector: SND -> {10 volume mount public:8,1 0 0}
↑ 发送挂载命令
06-10 11:13:05.257 D VoldConnector: RCV <- {651 public:8,1 2}
↑ volume 状态变为 2(STATE_MOUNTED)
06-10 11:13:05.257 D VoldConnector: RCV <- {200 10 Command succeeded}
↑ 命令成功
8.3 ResponseCode 常量
| Code | 含义 | 示例 |
|---|---|---|
| 200 | 命令成功 | {200 10 Command succeeded} |
| 400-499 | 一般错误 | 命令失败 |
| 640 | DiskCreated | {640 disk:8,0 8} |
| 641 | DiskSizeChanged | {641 disk:8,0 123009761280} |
| 642 | DiskLabelChanged | {642 disk:8,0 USB} |
| 643 | DiskScanned | {643 disk:8,0} |
| 644 | DiskSysPathChanged | {644 disk:8,0 /sys/...} |
| 649 | DiskDestroyed | {649 disk:8,0} |
| 650 | VolumeCreated | {650 public:8,1 0 "disk:8,0" ""} |
| 651 | VolumeStateChanged | {651 public:8,1 2} |
| 652 | VolumeFsTypeChanged | {652 public:8,1 vfat} |
| 653 | VolumeFsUuidChanged | {653 public:8,1 AECD-6E85} |
| 654 | VolumeFsLabelChanged | {654 public:8,1 USB} |
| 655 | VolumePathChanged | {655 public:8,1 /mnt/media_rw/Udisk} |
| 656 | VolumeInternalPathChanged | {656 public:8,1 /mnt/media_rw/Udisk} |
| 659 | VolumeDestroyed | {659 public:8,1} |
九、与 Java 层的衔接
vold 通过以下方式与 Java 层交互:
- NDC 回调:vold 主动发送事件(如
{640 disk:8,0 8})到 MountService 的NativeDaemonConnector - NDC 命令:MountService 通过 NDC 发送命令(如
{10 volume mount public:8,1 0 0})到 vold 的CommandListener
MountService (Java) vold (Native)
───────────────── ──────────────
NativeDaemonConnector
│── 发送命令 ──────────────────► CommandListener
│ │
│ ▼
│ VolumeManager
│ │
│◄── 接收回调 ◄────────────────── notifyEvent()
在不支持 Binder 的早期 Android 版本中,NDC 是 Native 守护进程与 Java 服务通信的主要方式。
十、关键源码文件索引
system/vold/
├── main.cpp ★ vold 入口
├── NetlinkManager.cpp ★ netlink socket 创建与监听
├── NetlinkHandler.cpp ★ uevent 路由
├── VolumeManager.cpp ★ 卷管理核心
├── CommandListener.cpp ★ NDC 命令处理
├── Disk.cpp ★ 磁盘模型、分区表解析(sgdisk --android-dump)
├── Disk.h
├── PublicVolume.cpp ★ 公共卷挂载(vfat → mount → FUSE)
├── PublicVolume.h
├── PrivateVolume.cpp ★ 私有卷(加密可收养存储)
├── PrivateVolume.h
├── EmulatedVolume.cpp ★ 模拟卷(/data/media)
├── EmulatedVolume.h
├── VolumeBase.cpp ★ Volume 基类(状态机:create/mount/unmount/destroy)
├── VolumeBase.h
├── fs/
│ ├── Vfat.cpp ★ FAT32 文件系统操作
│ ├── Vfat.h
│ ├── Ext4.cpp ★ ext4 文件系统操作
│ └── Ext4.h
└── ResponseCode.h ★ NDC 响应码定义
frameworks/base/services/core/java/com/android/server/
└── NativeDaemonConnector.java ★ Java 层 NDC 客户端
十一、小结
本文拆解了 vold 处理 U 盘插入的完整 Native 层流程:
- NetlinkManager::start() 创建 netlink socket 开始监听内核 uevent
- NetlinkHandler::onEvent() 过滤
SUBSYSTEM=block事件,路由到 VolumeManager - VolumeManager::handleBlockEvent() 只处理
DEVTYPE=disk事件,创建 Disk 对象(分区事件被忽略,由 sgdisk 扫描替代) - Disk::readPartitions() 通过
sgdisk --android-dump读取分区表,为每个分区创建 Volume - PublicVolume::doMount() 执行 blkid 检测 → fsck 检查 → mount(2) 挂载 → FUSE 权限管理
- 通过 NDC 协议与 Java 层 MountService 通信
从实际日志可以看到完整的 NDC 通信序列,从 {640 disk:8,0 8} 到 {651 public:8,1 2} 再到 {200 10 Command succeeded}。下一篇我们将深入 MountService,看 Java 层如何接收这些事件并发送广播。
更多推荐


所有评论(0)