Vold(Volume Daemon)即卷守护进程,是Android系统中具有root权限的Native 级存储守护进程 ,处于Kernel和Framework之间,是连接底层硬件与上层应用的重要桥梁,主要为应用存储文件提供服务。
核心职责:
- 监听内核Netlink Uevent事件,实时检测SD卡、U盘等外部存储设备的热插拔;
- 管理存储磁盘(Disk)、存储卷(Volume)的生命周期,完成分区、格式化、挂载、卸载、加密等底层操作;
- 实现文件系统检测(fsck)、权限配置、FUSE/Sdcardfs文件系统挂载;
- 通过Binder向Framework层提供存储操作接口,上报存储状态变更事件。
一、整体架构
Vold的架构可以分为五个层次,各层之间相互协作,共同完成存储管理的任务:
- Framework层(Java) :包含StorageManagerService和IStorageManager。StorageManagerService是系统服务,提供存储管理的Java API,为应用和其他系统组件提供访问存储相关功能的接口;IStorageManager是AIDL接口,定义了Framework层的存储服务接口,实现了Framework层与Native层之间的通信规范。
- Native Binder层(C++) :主要包括VoldNativeService、IVold和IVoldListener。VoldNativeService是实现IVold接口的Binder服务,是Vold对外提供服务的入口;IVold是AIDL接口,定义了从Framework到Native的跨进程调用接口;IVoldListener是AIDL接口,用于Vold向Framework发送事件回调,让Framework层可以及时感知到存储设备的状态变化。
- Vold核心层(C++) :包含VolumeManager、NetlinkManager和NetlinkHandler。VolumeManager是Vold的核心管理器(单例),管理所有Disk和Volume对象,负责解析NetlinkManager收到的消息,并做出相应决策;NetlinkManager负责监听内核的Netlink Uevent事件,内部维护着一个socket连接,接收来自内核的Uevent消息;NetlinkHandler处理Netlink事件,将设备插拔事件转换为Volume操作。
- 存储模型层(C++) :包括Disk、VolumeBase及各类具体Volume实现。Disk是物理磁盘的抽象,如SD卡、USB存储等;VolumeBase是Volume的基类,提供通用的挂载/卸载接口,各类具体Volume如PublicVolume、PrivateVolume、EmulatedVolume等继承自VolumeBase,实现了不同类型存储卷的具体功能。
- 底层支持 :主要包括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类型目录进行加密和解密操作:
- DE 目录加密解密 :DE目录在设备启动时就会被解密,不需要用户输入解锁凭证。Vold在系统启动过程中,会对DE目录进行解密操作,让系统可以访问其中的关键数据,如系统设置、应用程序等。
- CE 目录加密解密 :CE目录需要用户输入解锁凭证(如密码、图案、指纹等)才能被解密。当用户解锁设备时,Vold会根据用户输入的解锁凭证对CE目录进行解密操作,让用户可以访问其中的数据,如用户的照片、文档等。当用户锁定设备时,Vold会对CE目录进行加密操作,保障数据的安全性。
Vold通过与加密服务(如GateKeeper、Keystore等)的交互,实现对CE和DE目录的加密解密。在加密过程中,Vold会使用加密算法对目录中的数据进行加密,并将加密密钥存储在安全的地方;在解密过程中,Vold会根据用户输入的解锁凭证获取加密密钥,然后使用该密钥对目录中的数据进行解密。
4.4 跨进程通信机制
Vold通过Binder机制实现与Framework层的跨进程通信,具体流程如下:
- VoldNativeService注册 :Vold启动时,会将VoldNativeService注册到ServiceManager中,其他进程可以通过ServiceManager获取VoldNativeService的binder代理。
- 上层服务获取代理 :StorageManagerService等上层服务通过ServiceManager获取VoldNativeService的binder代理,然后可以通过该代理调用Vold提供的接口。
- 接口调用与响应 :上层服务调用VoldNativeService的接口时,会将请求参数封装成Parcel对象,并通过Binder机制发送给VoldNativeService。VoldNativeService接收到请求后,会解析Parcel对象,调用相应的功能模块进行处理,并将处理结果封装成Parcel对象,通过Binder机制返回给上层服务。
- 事件回调 :当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