系列目录第一篇:全景图与调用链路概览 | 第二篇:内核层—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
    }

四个关键设计要点:

  1. 独立线程模型SocketListener::startListener() 内部通过 pthread_create() 创建一个专用线程(SocketListener.cpp:103),专门用 select() 轮询 netlink socket。main 线程继续执行后续的 coldboot()CommandListener 启动,两者互不阻塞。

  2. 继承链NetlinkHandler → NetlinkListener → SocketListenerNetlinkHandler 构造函数将 netlink socket fd 传给 SocketListener(socket, false),第二个参数 false 表示该 socket 不需要 accept()(netlink socket 是无需连接的),直接作为客户端加入 mClients 列表。

  3. 线程安全onDataAvailable()onEvent()handleBlockEvent() 全部在监听线程中执行。VolumeManager::handleBlockEvent() 内部的 std::lock_guard<std::mutex> lock(mLock) 保障了与 NDC 命令处理线程之间的互斥。

  4. 数据流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 mbrDISK 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 层交互:

  1. NDC 回调:vold 主动发送事件(如 {640 disk:8,0 8})到 MountService 的 NativeDaemonConnector
  2. 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 层流程:

  1. NetlinkManager::start() 创建 netlink socket 开始监听内核 uevent
  2. NetlinkHandler::onEvent() 过滤 SUBSYSTEM=block 事件,路由到 VolumeManager
  3. VolumeManager::handleBlockEvent() 只处理 DEVTYPE=disk 事件,创建 Disk 对象(分区事件被忽略,由 sgdisk 扫描替代)
  4. Disk::readPartitions() 通过 sgdisk --android-dump 读取分区表,为每个分区创建 Volume
  5. PublicVolume::doMount() 执行 blkid 检测 → fsck 检查 → mount(2) 挂载 → FUSE 权限管理
  6. 通过 NDC 协议与 Java 层 MountService 通信

从实际日志可以看到完整的 NDC 通信序列,从 {640 disk:8,0 8}{651 public:8,1 2} 再到 {200 10 Command succeeded}。下一篇我们将深入 MountService,看 Java 层如何接收这些事件并发送广播。

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐