安卓vold服务

Vold(Volume Daemon)即卷守护进程,是Android系统中具有root权限的Native 级存储守护进程 ,处于Kernel和Framework之间,是连接底层硬件与上层应用的重要桥梁,主要为应用存储文件提供服务。

核心职责:

  1. 监听内核Netlink Uevent事件,实时检测SD卡、U盘等外部存储设备的热插拔;
  2. 管理存储磁盘(Disk)、存储卷(Volume)的生命周期,完成分区、格式化、挂载、卸载、加密等底层操作;
  3. 实现文件系统检测(fsck)、权限配置、FUSE/Sdcardfs文件系统挂载;
  4. 通过Binder向Framework层提供存储操作接口,上报存储状态变更事件。

一、整体架构

Vold的架构可以分为五个层次,各层之间相互协作,共同完成存储管理的任务:

  1. Framework层(Java) :包含StorageManagerService和IStorageManager。StorageManagerService是系统服务,提供存储管理的Java API,为应用和其他系统组件提供访问存储相关功能的接口;IStorageManager是AIDL接口,定义了Framework层的存储服务接口,实现了Framework层与Native层之间的通信规范。
  2. Native Binder层(C++) :主要包括VoldNativeService、IVold和IVoldListener。VoldNativeService是实现IVold接口的Binder服务,是Vold对外提供服务的入口;IVold是AIDL接口,定义了从Framework到Native的跨进程调用接口;IVoldListener是AIDL接口,用于Vold向Framework发送事件回调,让Framework层可以及时感知到存储设备的状态变化。
  3. Vold核心层(C++) :包含VolumeManager、NetlinkManager和NetlinkHandler。VolumeManager是Vold的核心管理器(单例),管理所有Disk和Volume对象,负责解析NetlinkManager收到的消息,并做出相应决策;NetlinkManager负责监听内核的Netlink Uevent事件,内部维护着一个socket连接,接收来自内核的Uevent消息;NetlinkHandler处理Netlink事件,将设备插拔事件转换为Volume操作。
  4. 存储模型层(C++) :包括Disk、VolumeBase及各类具体Volume实现。Disk是物理磁盘的抽象,如SD卡、USB存储等;VolumeBase是Volume的基类,提供通用的挂载/卸载接口,各类具体Volume如PublicVolume、PrivateVolume、EmulatedVolume等继承自VolumeBase,实现了不同类型存储卷的具体功能。
  5. 底层支持 :主要包括Kernel Netlink、块设备节点和fstab配置。Kernel Netlink是内核Uevent机制,用于设备热插拔事件通知;块设备节点是存储设备在系统中的标识,Vold通过操作这些节点来实现对存储设备的管理;fstab是文件系统表配置,定义了存储设备的挂载点、文件系统类型等信息,Vold在启动和挂载存储设备时会参考这些配置。

二、vold服务启动

Vold服务的启动始于Android系统的init进程,init进程是所有用户态进程的祖先。在init.rc配置文件中(system/vold/vold.rc),定义了Vold服务的启动指令和参数配置:

复制代码
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
    ioprio be 2
    task_profiles ProcessCapacityHigh
    shutdown critical
    group root reserved_disk
    user root
    reboot_on_failure reboot,vold-failed

这个配置告诉init进程,Vold进程以root权限运行,属于core类服务,在系统启动早期就会被加载。如果Vold意外终止,系统会尝试重启它。

Vold进程的main函数(system/vold/main.cpp)会执行以下初始化步骤:

复制代码
int main(int argc, char** argv) {
   atrace_set_tracing_enabled(false);
   setenv("ANDROID_LOG_TAGS", "*:d", 1);  // Do not submit with verbose logs enabled
   android::base::InitLogging(argv, &VoldLogger);

   LOG(INFO) << "Vold 3.0 (the awakening) firing up";

   ATRACE_BEGIN("main");

   LOG(DEBUG) << "Detected support for:"
              << (android::vold::IsFilesystemSupported("ext4") ? " ext4" : "")
              << (android::vold::IsFilesystemSupported("f2fs") ? " f2fs" : "")
              << (android::vold::IsFilesystemSupported("vfat") ? " vfat" : "");

   VolumeManager* vm;
   NetlinkManager* nm;

   parse_args(argc, argv);

   sehandle = selinux_android_file_context_handle();
   if (!sehandle) {
       LOG(ERROR) << "Failed to get SELinux file contexts handle";
       exit(1);
   }
   selinux_android_set_sehandle(sehandle);

   mkdir("/dev/block/vold", 0755);

   /* For when cryptfs checks and mounts an encrypted filesystem */
   klog_set_level(6);

   /* Create our singleton managers */
   if (!(vm = VolumeManager::Instance())) {
       LOG(ERROR) << "Unable to create VolumeManager";
        exit(1);
    }

    if (!(nm = NetlinkManager::Instance())) {
        LOG(ERROR) << "Unable to create NetlinkManager";
        exit(1);
    }

    if (android::base::GetBoolProperty("vold.debug", false)) {
        vm->setDebug(true);
    }

    if (vm->start()) {
        PLOG(ERROR) << "Unable to start VolumeManager";
        exit(1);
    }

    VoldConfigs configs = {};
    if (process_config(vm, &configs)) {
        PLOG(ERROR) << "Error reading configuration... continuing anyways";
    }

    android::hardware::configureRpcThreadpool(1, false /* callerWillJoin */);

    ATRACE_BEGIN("VoldNativeService::start");
    if (android::vold::VoldNativeService::start() != android::OK) {
        LOG(ERROR) << "Unable to start VoldNativeService";
        exit(1);
    }
    ATRACE_END();
    LOG(DEBUG) << "VoldNativeService::start() completed OK";

    ATRACE_BEGIN("VendorVoldNativeService::try_start");
    if (android::vold::VendorVoldNativeService::try_start() != android::OK) {
        LOG(ERROR) << "Unable to start VendorVoldNativeService";
        exit(1);
    }
    ATRACE_END();
    LOG(DEBUG) << "VendorVoldNativeService::try_start() completed OK";

    ATRACE_BEGIN("NetlinkManager::start");
    if (nm->start()) {
        PLOG(ERROR) << "Unable to start NetlinkManager";
        exit(1);
    }
    ATRACE_END();

    android::base::SetProperty("vold.has_adoptable", configs.has_adoptable ? "1" : "0");
    android::base::SetProperty("vold.has_quota", configs.has_quota ? "1" : "0");
    android::base::SetProperty("vold.has_reserved", configs.has_reserved ? "1" : "0");
    android::base::SetProperty("vold.has_compress", configs.has_compress ? "1" : "0");

    coldboot("/sys/block");

    ATRACE_END();

    android::IPCThreadState::self()->joinThreadPool();
    LOG(INFO) << "vold shutting down";

    exit(0);
}

1、解析命令行参数 - parse_args :解析启动Vold时传入的命令行参数,如blkid_context、fsck_context等。

复制代码
//parse_args
static void parse_args(int argc, char** argv) {
    static struct option opts[] = {
        {"blkid_context", required_argument, 0, 'b'},
        {"blkid_untrusted_context", required_argument, 0, 'B'},
        {"fsck_context", required_argument, 0, 'f'},
        {"fsck_untrusted_context", required_argument, 0, 'F'},
        {nullptr, 0, nullptr, 0},
    };

    int c;
    while ((c = getopt_long(argc, argv, "", opts, nullptr)) != -1) {
        switch (c) {
            // clang-format off
        case 'b': android::vold::sBlkidContext = optarg; break;
        case 'B': android::vold::sBlkidUntrustedContext = optarg; break;
        case 'f': android::vold::sFsckContext = optarg; break;
        case 'F': android::vold::sFsckUntrustedContext = optarg; break;
                // clang-format on
        }
    }

    CHECK(android::vold::sBlkidContext != nullptr);
    CHECK(android::vold::sBlkidUntrustedContext != nullptr);
    CHECK(android::vold::sFsckContext != nullptr);
    CHECK(android::vold::sFsckUntrustedContext != nullptr);
}

2、创建必要的目录 - mkdir("/dev/block/vold", 0755) :创建/dev/block/vold目录,用于存储块设备节点。

3、创建核心管理器实例 - VolumeManager::Instance()&NetlinkManager::Instance() :创建VolumeManager和NetlinkManager的实例,这两个管理器都采用单例模式,确保全局只有一个实例。

4、启动VolumeManager - vm->start() :调用VolumeManager的start()方法,VolumeManager会先卸载掉对应文件夹中的所有东西,使之处于一个干净的状态,然后创建内置存储目录(/data/media),并设置当前存储设备的状态为unmounted。

复制代码
int VolumeManager::start() {
    ATRACE_NAME("VolumeManager::start");

    // Always start from a clean slate by unmounting everything in
    // directories that we own, in case we crashed.
    unmountAll();

    Loop::destroyAll();

    // Assume that we always have an emulated volume on internal
    // storage; the framework will decide if it should be mounted.
    CHECK(mInternalEmulatedVolumes.empty());

    auto vol = std::shared_ptr<android::vold::VolumeBase>(
            new android::vold::EmulatedVolume("/data/media", 0));
    vol->setMountUserId(0);
    vol->create();
    mInternalEmulatedVolumes.push_back(vol);

    // Consider creating a virtual disk
    updateVirtualDisk();

    return 0;
}

5、处理存储配置文件 - process_config :解析fstab配置文件,获取存储设备的挂载点、文件系统类型等信息。

6、配置RPC线程池 :调用android::hardware::configureRpcThreadpool()方法,配置RPC线程池,为跨进程通信提供支持。

7、启动VoldNativeService - VoldNativeService::start() :调用android::vold::VoldNativeService::start()方法,注册VoldNativeService接口,使其他服务可以通过IVold找到Vold的binder代理。

复制代码
status_t VoldNativeService::start() {
    IPCThreadState::self()->disableBackgroundScheduling(true);
    status_t ret = BinderService<VoldNativeService>::publish();
    if (ret != android::OK) {
        return ret;
    }
    sp<ProcessState> ps(ProcessState::self());
    ps->startThreadPool();
    ps->giveThreadPoolName();
    return android::OK;
}

8、启动NetlinkManager - nm->start() :调用NetlinkManager的start()方法,启动Uevent监听,接收来自内核的Uevent消息。

复制代码
int NetlinkManager::start() {
    struct sockaddr_nl nladdr;
    int sz = 64 * 1024;
    int on = 1;

    memset(&nladdr, 0, sizeof(nladdr));
    nladdr.nl_family = AF_NETLINK;
    nladdr.nl_pid = getpid();
    nladdr.nl_groups = 0xffffffff;

    if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) < 0) {
        PLOG(ERROR) << "Unable to create uevent socket";
        return -1;
    }

    // When running in a net/user namespace, SO_RCVBUFFORCE will fail because
    // it will check for the CAP_NET_ADMIN capability in the root namespace.
    // Try using SO_RCVBUF if that fails.
    if ((setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) &&
        (setsockopt(mSock, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz)) < 0)) {
        PLOG(ERROR) << "Unable to set uevent socket SO_RCVBUF/SO_RCVBUFFORCE option";
        goto out;
    }

    if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
        PLOG(ERROR) << "Unable to set uevent socket SO_PASSCRED option";
        goto out;
    }

    if (bind(mSock, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0) {
        PLOG(ERROR) << "Unable to bind uevent socket";
        goto out;
    }

    mHandler = new NetlinkHandler(mSock);
    if (mHandler->start()) {
        PLOG(ERROR) << "Unable to start NetlinkHandler";
        goto out;
    }

    return 0;

out:
    close(mSock);
    return -1;
}

9、设置系统属性 :设置vold.has_adoptable、vold.has_quota、vold.has_reserved等系统属性,标识系统是否支持可采用存储、磁盘配额等功能。

10、冷启动扫描 :调用coldboot()方法,扫描/sys/block目录,检测已连接的存储设备。

11、加入线程池 :调用android::IPCThreadState::self()->joinThreadPool()方法,让Vold进程进入线程池,等待处理来自其他进程的请求。

三、核心组件

NetlinkManager :是Vold的"耳朵",同样采用单例模式。它内部建立了socket连接,主要作用是接收来自Kernel的Uevent消息。当用户插入或拔出存储设备时,内核会通过Uevent机制发送通知,NetlinkManager的socket接收到这些原始消息后,会将消息传递给NetlinkHandler进行处理。

VolumeManager :作为Vold的"大脑",采用单例模式,确保全局只有一个管理器实例。它负责管理所有的Disk和Volume对象,当NetlinkManager接收到内核的Uevent事件后,会将事件传递给VolumeManager,VolumeManager解析这些事件,并根据事件类型做出相应的处理,如创建或删除Volume对象、挂载或卸载存储卷等。

VoldNativeService :是Vold对外提供的"服务窗口",继承自BinderService类。它定义了IVold接口,让StorageManagerService等上层服务能够调用Vold的功能。VoldNativeService在启动过程中会注册接口,使其他服务可以通过IVold找到Vold的binder代理,进而与Vold进行通信。

各类Volume实现

  • PublicVolume :公共存储卷,通常采用FAT32/exFAT格式,如SD卡、U盘等可移动存储设备。这类存储卷可以被多个应用访问,用户可以方便地在不同设备之间传输数据。
  • PrivateVolume :可采用存储卷,采用ext4加密格式。用户可以将SD卡格式化为可采用存储,使其成为内部存储的一部分,系统会对其进行加密,保障数据的安全性。
  • EmulatedVolume :模拟存储卷,路径为/storage/emulated,用于模拟SD卡。由于历史原因,早期的Android设备没有内置SD卡,通过模拟存储卷可以让应用在没有物理SD卡的情况下也能使用外部存储功能。
  • ObbVolume :OBB扩展文件卷,用于管理应用的OBB扩展文件。OBB文件通常包含应用的额外资源,如游戏的数据包等,Vold负责将OBB文件挂载到指定路径,让应用可以访问其中的资源。

四、核心工作机制

4.1 存储设备检测与事件处理

当用户插入或拔出存储设备时,内核会检测到硬件变化,并通过Uevent机制发送通知。Vold的NetlinkManager会监听这些Uevent事件,具体处理流程如下:

1、内核发送Uevent事件 :当存储设备插入或拔出时,内核会生成相应的Uevent事件,并通过Netlink套接字发送给用户空间。

2、NetlinkManager接收事件 :NetlinkManager内部维护着一个socket连接,当接收到内核发送的Uevent事件后,会将事件传递给NetlinkHandler进行处理。

3、NetlinkHandler解析事件 :NetlinkHandler会解析Uevent事件,提取事件类型、设备路径等信息,并将这些信息转换为Volume操作。

复制代码
void NetlinkHandler::onEvent(NetlinkEvent* evt) {
    VolumeManager* vm = VolumeManager::Instance();
    const char* subsys = evt->getSubsystem();

    if (!subsys) {
        LOG(WARNING) << "No subsystem found in netlink event";
        return;
    }

    if (std::string(subsys) == "block") {
        vm->handleBlockEvent(evt);
    }
}

4、VolumeManager处理事件 :VolumeManager接收到NetlinkHandler传递的事件后,会根据事件类型做出相应的处理。例如,当检测到存储设备插入事件时,VolumeManager会创建对应的Disk和Volume对象,并尝试挂载该存储设备;当检测到存储设备拔出事件时,VolumeManager会卸载该存储设备,并删除对应的Disk和Volume对象。

复制代码
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") : "");

    if (devType != "disk") return;

    int major = std::stoi(evt->findParam("MAJOR"));
    int minor = std::stoi(evt->findParam("MINOR"));
    dev_t device = makedev(major, minor);

    switch (evt->getAction()) {
        case NetlinkEvent::Action::kAdd: {
            for (const auto& source : mDiskSources) {
                if (source->matches(eventPath)) {
                    // For now, assume that MMC and virtio-blk (the latter is
                    // specific to virtual platforms; see Utils.cpp for details)
                    // devices are SD, and that everything else is USB
                    int flags = source->getFlags();
                    if (major == kMajorBlockMmc || IsVirtioBlkDevice(major)) {
                        flags |= android::vold::Disk::Flags::kSd;
                    } else {
                        flags |= android::vold::Disk::Flags::kUsb;
                    }

                    auto disk =
                        new android::vold::Disk(eventPath, device, source->getNickname(), flags);
                    handleDiskAdded(std::shared_ptr<android::vold::Disk>(disk));
                    break;
                }
            }
            break;
        }
        case NetlinkEvent::Action::kChange: {
            LOG(VERBOSE) << "Disk at " << major << ":" << minor << " changed";
            handleDiskChanged(device);
            break;
        }
        case NetlinkEvent::Action::kRemove: {
            handleDiskRemoved(device);
            break;
        }
        default: {
            LOG(WARNING) << "Unexpected block event action " << (int)evt->getAction();
            break;
        }
    }
}

以插入事件为例:

复制代码
void VolumeManager::handleDiskAdded(const std::shared_ptr<android::vold::Disk>& disk) {
    // For security reasons, if secure keyguard is showing, wait
    // until the user unlocks the device to actually touch it
    // Additionally, wait until user 0 is actually started, since we need
    // the user to be up before we can mount a FUSE daemon to handle the disk.
    bool userZeroStarted = mStartedUsers.find(0) != mStartedUsers.end();
    if (mSecureKeyguardShowing) {
        LOG(INFO) << "Found disk at " << disk->getEventPath()
                  << " but delaying scan due to secure keyguard";
        mPendingDisks.push_back(disk);
    } else if (!userZeroStarted) {
        LOG(INFO) << "Found disk at " << disk->getEventPath()
                  << " but delaying scan due to user zero not having started";
        mPendingDisks.push_back(disk);
    } else {
        disk->create();
        mDisks.push_back(disk);
    }
}

==》Disk.create()

复制代码
status_t Disk::create() {
    CHECK(!mCreated);
    mCreated = true;

    auto listener = VolumeManager::Instance()->getListener();
    if (listener) listener->onDiskCreated(getId(), mFlags);

    if (isStub()) {
        createStubVolume();
        return OK;
    }
    // 读取磁盘的元数据(如标签、UUID等基本信息)
    readMetadata();
     // 扫描并读取磁盘上的分区表信息,构建分区对象列表
    readPartitions();
    return OK;
}

5、通知Framework层 :VolumeManager处理完事件后,会通过VoldNativeService和IVoldListener接口将事件通知给StorageManagerService,StorageManagerService再将事件通知给上层应用,让应用可以感知到存储设备的状态变化。

上面插入事件调用Disk.create函数中:listener->onDiskCreated 即是通知给StorageManagerService

4.2 存储卷管理流程

4.2.1 卷的挂载流程

以SD卡插入为例,存储卷的挂载流程如下:

1、设备检测与事件处理 :如上述存储设备检测与事件处理流程,Vold检测到SD卡插入事件,并创建对应的Disk和Volume对象,根据存储设备的类型、文件系统类型等信息创建volume类型。

复制代码
else if (*it == "PART") {
	foundParts = true;

	if (++it == split.end()) continue;
	int i = 0;
	if (!android::base::ParseInt(*it, &i, 1, maxMinors)) {
		LOG(WARNING) << "Invalid partition number " << *it;
		continue;
	}
	dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);

	if (table == Table::kMbr) {
		if (++it == split.end()) continue;
		int type = 0;
		if (!android::base::ParseInt("0x" + *it, &type)) {
			LOG(WARNING) << "Invalid partition type " << *it;
			continue;
		}

		switch (type) {
			case 0x06:  // FAT16
			case 0x07:  // HPFS/NTFS/exFAT
			case 0x0b:  // W95 FAT32 (LBA)
			case 0x0c:  // W95 FAT32 (LBA)
			case 0x0e:  // W95 FAT16 (LBA)
				createPublicVolume(partDevice);
				break;
		}
	} else if (table == Table::kGpt) {
		if (++it == split.end()) continue;
		auto typeGuid = *it;
		if (++it == split.end()) continue;
		auto partGuid = *it;

		if (android::base::EqualsIgnoreCase(typeGuid, kGptBasicData)) {
			createPublicVolume(partDevice);
		} else if (android::base::EqualsIgnoreCase(typeGuid, kGptAndroidExpand)) {
			createPrivateVolume(partDevice, partGuid);
		}
	}
}

void Disk::createPublicVolume(dev_t device) {
    auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(device));
    if (mJustPartitioned) {
        LOG(DEBUG) << "Device just partitioned; silently formatting";
        vol->setSilent(true);
        vol->create();
        vol->format("auto");
        vol->destroy();
        vol->setSilent(false);
    }

    mVolumes.push_back(vol);
    vol->setDiskId(getId());
    vol->create();
}

status_t VolumeBase::create() {
    CHECK(!mCreated);

    mCreated = true;
    status_t res = doCreate();

    auto listener = getListener();
    if (listener) {
        listener->onVolumeCreated(getId(), static_cast<int32_t>(mType), mDiskId, mPartGuid,
                                  mMountUserId);
    }

    setState(State::kUnmounted);
    return res;
}

2、 执行挂载操作 :StorageManagerService在收到onVolumeCreated后会下发挂载指令至VoldNativeService,调用Volume的mount()方法,Volume对象会根据自身类型执行具体的挂载操作。

复制代码
binder::Status VoldNativeService::mount(
        const std::string& volId, int32_t mountFlags, int32_t mountUserId,
        const android::sp<android::os::IVoldMountCallback>& callback) {
    ENFORCE_SYSTEM_OR_ROOT;
    CHECK_ARGUMENT_ID(volId);
    ACQUIRE_LOCK;

    auto vol = VolumeManager::Instance()->findVolume(volId);
    if (vol == nullptr) {
        return error("Failed to find volume " + volId);
    }

    vol->setMountFlags(mountFlags);
    vol->setMountUserId(mountUserId);

    vol->setMountCallback(callback);
    int res = vol->mount();
    vol->setMountCallback(nullptr);

    if (res != OK) {
        return translate(res);
    }

    return translate(OK);
}

status_t VolumeBase::mount() {
    if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
        LOG(WARNING) << getId() << " mount requires state unmounted or unmountable";
        return -EBUSY;
    }

    setState(State::kChecking);
    status_t res = doMount();
    setState(res == OK ? State::kMounted : State::kUnmountable);

    if (res == OK) {
        doPostMount();
    }
    return res;
}

对于PublicVolume,会执行以下步骤:

  • 检查文件类型是否支持

    if (mFsType == "vfat" && vfat::IsSupported()) {
    if (vfat::Check(mDevPath)) {
    LOG(ERROR) << getId() << " failed filesystem check";
    return -EIO;
    }
    } else if (mFsType == "exfat" && exfat::IsSupported()) {
    if (exfat::Check(mDevPath)) {
    LOG(ERROR) << getId() << " failed filesystem check";
    return -EIO;
    }
    } else {
    LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;
    return -EIO;
    }

  • 创建挂载点目录,如果挂载点目录不存在,则创建该目录。设置挂载点的权限和所有者,让应用可以访问该存储设备。

    mRawPath = StringPrintf("/mnt/media_rw/%s", stableName.c_str());

    setInternalPath(mRawPath);
    if (isVisible) {
    setPath(StringPrintf("/storage/%s", stableName.c_str()));
    } else {
    setPath(mRawPath);
    }

    if (fs_prepare_dir(mRawPath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
    PLOG(ERROR) << getId() << " failed to create mount points";
    return -errno;
    }

  • 根据文件类型执行对应的挂载命令,将存储设备挂载到指定的挂载点。

    if (mFsType == "vfat") {
    if (vfat::Mount(mDevPath, mRawPath, false, false, false, AID_ROOT,
    (isVisible ? AID_MEDIA_RW : AID_EXTERNAL_STORAGE), 0007, true)) {
    PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
    return -EIO;
    }
    } else if (mFsType == "exfat") {
    if (exfat::Mount(mDevPath, mRawPath, AID_ROOT,
    (isVisible ? AID_MEDIA_RW : AID_EXTERNAL_STORAGE), 0007)) {
    PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
    return -EIO;
    }
    }

  • 如需FUSE/Sdcardfs,完成虚拟文件系统挂载,映射应用访问路径;

  • 标记卷为已挂载状态,调用onVolumeStateChanged通知Framework层

    //VolumeBase.cpp mount()
    setState(res == OK ? State::kMounted : State::kUnmountable);
    //setState中会回调状态给上层
    void VolumeBase::setState(State state) {
    mState = state;

    复制代码
      auto listener = getListener();
      if (listener) {
          listener->onVolumeStateChanged(getId(), static_cast<int32_t>(mState));
      }

    }

在每个流程中都会调用VolumeBase::setState调整当前的状态,并通过onVolumeStateChanged通知给上层

4.2.2 卷的卸载流程

当用户拔出SD卡时,存储卷的卸载流程如下:

1、设备检测与事件处理 :Vold检测到SD卡拔出事件,并通知VolumeManager。

2、 执行卸载操作 :VolumeManager调用Volume对象的unmount()方法。

复制代码
status_t VolumeBase::destroy() {
    CHECK(mCreated);

    if (mState == State::kMounted) {
        unmount();
        setState(State::kBadRemoval);
    } else {
        setState(State::kRemoved);
    }

    auto listener = getListener();
    if (listener) {
        listener->onVolumeDestroyed(getId());
    }

    status_t res = doDestroy();
    mCreated = false;
    return res;
}

Volume对象会执行以下步骤:

  • 检查存储设备是否正在被使用,如果正在被使用,则杀掉使用该存储设备的进程。

  • 执行卸载命令,将存储设备从挂载点卸载。

  • 删除挂载点目录,如果挂载点目录为空,则删除该目录。

    status_t PublicVolume::doUnmount() {
    // Unmount the storage before we kill the FUSE process. If we kill
    // the FUSE process first, most file system operations will return
    // ENOTCONN until the unmount completes. This is an exotic and unusual
    // error code and might cause broken behaviour in applications.
    KillProcessesUsingPath(getPath());

    复制代码
      if (mFuseMounted) {
          // Use UUID as stable name, if available
          std::string stableName = getId();
          if (!mFsUuid.empty()) {
              stableName = mFsUuid;
          }
    
          if (UnmountUserFuse(getMountUserId(), getInternalPath(), stableName) != OK) {
              PLOG(INFO) << "UnmountUserFuse failed on public fuse volume";
              return -errno;
          }
    
          mFuseMounted = false;
      }
    
      ForceUnmount(kAsecPath);
    
      if (mUseSdcardFs) {
          ForceUnmount(mSdcardFsDefault);
          ForceUnmount(mSdcardFsRead);
          ForceUnmount(mSdcardFsWrite);
          ForceUnmount(mSdcardFsFull);
    
          rmdir(mSdcardFsDefault.c_str());
          rmdir(mSdcardFsRead.c_str());
          rmdir(mSdcardFsWrite.c_str());
          rmdir(mSdcardFsFull.c_str());
    
          mSdcardFsDefault.clear();
          mSdcardFsRead.clear();
          mSdcardFsWrite.clear();
          mSdcardFsFull.clear();
      }
      ForceUnmount(mRawPath);
      rmdir(mRawPath.c_str());
      mRawPath.clear();
    
      return OK;

    }

  • 通知Framework层 :卸载操作完成后,VolumeManager会调用IVoldListener的onVolumeStateChanged接口将事件通知给StorageManagerService。(VolumeBase::setState中会调用IVoldListener的onVolumeStateChanged接口)

    if (mState == State::kMounted) {
    unmount();
    setState(State::kBadRemoval);
    } else {
    setState(State::kRemoved);
    }

4.2.3 卷的格式化操作

当用户需要格式化存储设备时,Vold会执行以下流程:

1、接收格式化请求 :上层应用通过StorageManager API向StorageManagerService发送格式化请求,StorageManagerService将请求发送给VoldNativeService。

2、格式化操作 :VoldNativeService将请求传递给VolumeManager,VolumeManager根据存储设备的类型和状态,执行格式化操作。

复制代码
binder::Status VoldNativeService::format(const std::string& volId, const std::string& fsType) {
    ENFORCE_SYSTEM_OR_ROOT;
    CHECK_ARGUMENT_ID(volId);
    ACQUIRE_LOCK;

    auto vol = VolumeManager::Instance()->findVolume(volId);
    if (vol == nullptr) {
        return error("Failed to find volume " + volId);
    }
    return translate(vol->format(fsType));
}

VolumeManager调用Volume对象的format()方法,Volume对象会执行以下步骤:

  • 卸载存储设备,如果存储设备已经挂载,则先卸载该存储设备。

    status_t VolumeBase::format(const std::string& fsType) {
    if (mState == State::kMounted) {
    unmount();
    }

    复制代码
      if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
          LOG(WARNING) << getId() << " format requires state unmounted or unmountable";
          return -EBUSY;
      }
    
      setState(State::kFormatting);
      status_t res = doFormat(fsType);
      setState(State::kUnmounted);
      return res;

    }

  • 执行格式化命令,将存储设备格式化为指定的文件系统类型。

    status_t PublicVolume::doFormat(const std::string& fsType) {
    bool isVfatSup = vfat::IsSupported();
    bool isExfatSup = exfat::IsSupported();
    status_t res = OK;

    复制代码
      enum { NONE, VFAT, EXFAT } fsPick = NONE;
    
      // Resolve auto requests
      if (fsType == "auto" && isVfatSup && isExfatSup) {
          uint64_t size = 0;
    
          res = GetBlockDevSize(mDevPath, &size);
          if (res != OK) {
              LOG(ERROR) << "Couldn't get device size " << mDevPath;
              return res;
          }
    
          // If both vfat & exfat are supported use exfat for SDXC (>~32GiB) cards
          if (size > 32896LL * 1024 * 1024) {
              fsPick = EXFAT;
          } else {
              fsPick = VFAT;
          }
      } else if (fsType == "auto" && isExfatSup) {
          fsPick = EXFAT;
      } else if (fsType == "auto" && isVfatSup) {
          fsPick = VFAT;
      }
    
      // Resolve explicit requests
      if (fsType == "vfat" && isVfatSup) {
          fsPick = VFAT;
      } else if (fsType == "exfat" && isExfatSup) {
          fsPick = EXFAT;
      }
    
      if (WipeBlockDevice(mDevPath) != OK) {
          LOG(WARNING) << getId() << " failed to wipe";
      }
    
      if (fsPick == VFAT) {
          res = vfat::Format(mDevPath, 0);
      } else if (fsPick == EXFAT) {
          res = exfat::Format(mDevPath);
      } else {
          LOG(ERROR) << "Unsupported filesystem " << fsType;
          return -EINVAL;
      }
    
      if (res != OK) {
          LOG(ERROR) << getId() << " failed to format";
          res = -errno;
      }
    
      return res;

    }
    //以exfat为例,Format实现:
    status_t Format(const std::string& source) {
    std::vectorstd::string cmd;
    cmd.push_back(kMkfsPath);
    cmd.push_back("-n");
    cmd.push_back("android");
    cmd.push_back(source);

    复制代码
      int rc = ForkExecvp(cmd);
      if (rc == 0) {
          LOG(INFO) << "Format OK";
          return 0;
      } else {
          LOG(ERROR) << "Format failed (code " << rc << ")";
          errno = EIO;
          return -1;
      }
      return 0;

    }

  • 通知Framework层 :格式化操作完成后,VolumeManager会调用IVoldListener的onVolumeStateChanged接口将事件通知给StorageManagerService(VolumeBase::setState中会调用IVoldListener的onVolumeStateChanged接口)。在格式化前先更新状态为正在格式化State::kFormatting,格式化完成后,更新状态为卸载状态State::kUnmounted

    setState(State::kFormatting);
    status_t res = doFormat(fsType);
    setState(State::kUnmounted);

4.3 存储加密机制

Android系统中的内部存储设备加密分为DE(Device Encrypted)和CE(Credential Encrypted)两种,Vold负责对CE和DE类型目录进行加密和解密操作:

  1. DE 目录加密解密 :DE目录在设备启动时就会被解密,不需要用户输入解锁凭证。Vold在系统启动过程中,会对DE目录进行解密操作,让系统可以访问其中的关键数据,如系统设置、应用程序等。
  2. CE 目录加密解密 :CE目录需要用户输入解锁凭证(如密码、图案、指纹等)才能被解密。当用户解锁设备时,Vold会根据用户输入的解锁凭证对CE目录进行解密操作,让用户可以访问其中的数据,如用户的照片、文档等。当用户锁定设备时,Vold会对CE目录进行加密操作,保障数据的安全性。

Vold通过与加密服务(如GateKeeper、Keystore等)的交互,实现对CE和DE目录的加密解密。在加密过程中,Vold会使用加密算法对目录中的数据进行加密,并将加密密钥存储在安全的地方;在解密过程中,Vold会根据用户输入的解锁凭证获取加密密钥,然后使用该密钥对目录中的数据进行解密。

4.4 跨进程通信机制

Vold通过Binder机制实现与Framework层的跨进程通信,具体流程如下:

  1. VoldNativeService注册 :Vold启动时,会将VoldNativeService注册到ServiceManager中,其他进程可以通过ServiceManager获取VoldNativeService的binder代理。
  2. 上层服务获取代理 :StorageManagerService等上层服务通过ServiceManager获取VoldNativeService的binder代理,然后可以通过该代理调用Vold提供的接口。
  3. 接口调用与响应 :上层服务调用VoldNativeService的接口时,会将请求参数封装成Parcel对象,并通过Binder机制发送给VoldNativeService。VoldNativeService接收到请求后,会解析Parcel对象,调用相应的功能模块进行处理,并将处理结果封装成Parcel对象,通过Binder机制返回给上层服务。
  4. 事件回调 :当Vold检测到存储设备的状态变化时,会通过IVoldListener接口将事件通知给上层服务。上层服务需要实现IVoldListener接口,并将该接口的实例设置给VoldNativeService,以便接收事件通知。

五、关键技术特性

5.1 多用户存储隔离

系统为每个用户分配独立的存储命名空间,存储卷挂载路径包含用户ID(如/mnt/user/0/emulated),确保不同用户存储数据相互隔离,Vold负责用户存储目录创建、权限配置。

5.2 FUSE/Sdcardfs文件系统

为实现存储权限管控与路径映射,外置存储通过FUSE/Sdcardfs挂载:

  • 应用访问路径:/storage/emulated/0;
  • 底层实际路径:/data/media/0;
  • FUSE守护进程拦截文件操作,完成权限校验、UID/GID映射,保障存储安全。

5.3 配置文件

fstab是文件系统表配置文件,定义了存储设备的挂载点、文件系统类型、挂载选项等信息。对于Android 4.3及更高版本,init、vold和recovery所使用的各种fstab文件在.fstab文件中进行统一。对于由vold管理的外部存储卷,条目格式如下:

<src> <mnt_point> <type> <mnt_flags> <fs_mgr_flags>

其中,src是sysfs(通常在/sys下装载)下可以提供装载点的设备的路径,路径必须以/开头;mount_point是要装载卷的文件系统路径;type是卷上的文件系统类型,如果是外部卡,则通常为vfat;mnt_flags是Vold会忽略的字段,应将其设置为defaults;fs_mgr_flags是Vold会忽略此字段中不包含voldmanaged=标记的统一的fstab中的任何行,该标记必须后跟描述卡的标签,以及分区号或字词auto,例如voldmanaged=sdcard:auto,其他可能的标记包括nonremovable、encryptable=sdcard、noemulatedsd和encryptable=userdata。

完整的例子:

复制代码
# Android fstab file.
#<src>                                       <mnt_point>         <type>    <mnt_flags and options>       <fs_mgr_flags>
/dev/block/platform/ff0f0000.dwmmc/by-name/user                  /mnt/internal_sd     vfat             defaults                  defaults
相关推荐
开开心心就好2 小时前
体积小巧的图片重复查找工具推荐
linux·运维·服务器·智能手机·自动化·excel·fabric
明天就是Friday2 小时前
Android实战项目⑤ Paging 3开发社交媒体信息流App 完整源码详解
android·媒体
tryqaaa_2 小时前
学习日志(一)【含markdown语法,Linux学习】
linux·运维·学习·web安全·web·markdown
渔民小镇2 小时前
5 分钟搭建桌游服务器:Room 模块 + 领域事件实战
java·运维·服务器·分布式·游戏
小义_2 小时前
【Kubernetes】(七) 控制器2
linux·运维·云原生·kubernetes·红帽
WJ.Polar2 小时前
Ansible任务控制
linux·运维·网络·python·ansible
宋拾壹2 小时前
php网站小程序接入抖音团购核销
android·小程序·php
计算机安禾2 小时前
【Linux从入门到精通】第10篇:软件包管理——Linux如何安装与卸载软件
java·linux·运维·服务器·编辑器
zzzyyy5382 小时前
Linux进程控制(2)
linux·服务器