在Android 系统中所有的热插拔设备都是通过Vold 进程挂载的。通过kernel–>vold–>StorageManagerService这样的架构去逐级上报热插拔事件。

一、Vold 入口

--> /system/vold/main.cint main(int argc, char** argv) {atrace_set_tracing_enabled(false);setenv("ANDROID_LOG_TAGS", "*:v", 1);android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));LOG(INFO) << "Vold 3.0 (the awakening) firing up";ATRACE_BEGIN("main");LOG(VERBOSE) << "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) {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);}bool has_adoptable;bool has_quota;bool has_reserved;//解析fstab 文件if (process_config(vm, &has_adoptable, &has_quota, &has_reserved)) {PLOG(ERROR) << "Error reading configuration... continuing anyways";}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("NetlinkManager::start");if (nm->start()) {PLOG(ERROR) << "Unable to start NetlinkManager";exit(1);}ATRACE_END();// This call should go after listeners are started to avoid// a deadlock between vold and init (see b/34278978 for details)android::base::SetProperty("vold.has_adoptable", has_adoptable ? "1" : "0");android::base::SetProperty("vold.has_quota", has_quota ? "1" : "0");android::base::SetProperty("vold.has_reserved", has_reserved ? "1" : "0");// Do coldboot here so it won't block booting,// also the cold boot is needed in case we have flash drive// connected before Vold launchedcoldboot("/sys/block");ATRACE_END();android::IPCThreadState::self()->joinThreadPool();LOG(INFO) << "vold shutting down";exit(0);
}

main 函数里面 做以下几件事:
1、创建NetLinkManager对象
2、创建VolumeManager对象
3、process_config 函数里面解析 fstab 文件

二、NetLinkManager

NetLinkManager 是接受Uevent 事件的上报,最终干活的是NetlinkHandler。

三、VolumeManager

Voulemanager 是处理所有挂载事件的,统一在这里根据事件的类型进行分发。

四、解析fstab 文件

4.1、fstab 文件是存放的是系统中的文件系统信息的,一般位于源码中device 目录下面,在设备的vendor/etc/fstab.xxx 下。如下截图的这两行是usb 设备的挂载信息。
src 表示 待挂载的设备节点路径
mount point表示 挂载点,即 被挂载的目录
filesystem type表示 所挂载磁盘的文件系统类型
mount flags parameters表示 指定所挂载的文件系统的一些参数,如下

#<src>   <mnt_point>    <type>    <mnt_flags and options>   <fs_mgr_flags>
/devices/platform/passthrough/5b0d0000.usb/ci_hdrc.0/* auto  auto  defaults  voldmanaged=usb:auto
/devices/platform/5b0d0000.usb/ci_hdrc.0/* auto  auto  defaults  voldmanaged=usb:auto

4.2、当插入 USB 设备后会在/sys/devices/platform/5b0d0000.usb/ci_hdrc.0 下面生成对应的usb 设备节点。接下来看看process_config解析 函数

static int process_config(VolumeManager* vm, bool* has_adoptable, bool* has_quota,bool* has_reserved) {ATRACE_NAME("process_config");fstab_default = fs_mgr_read_fstab_default();if (!fstab_default) {PLOG(ERROR) << "Failed to open default fstab";return -1;}/* Loop through entries looking for ones that vold manages */*has_adoptable = false;*has_quota = false;*has_reserved = false;for (int i = 0; i < fstab_default->num_entries; i++) {auto rec = &fstab_default->recs[i];if (fs_mgr_is_quota(rec)) {*has_quota = true;}if (rec->reserved_size > 0) {*has_reserved = true;}if (fs_mgr_is_voldmanaged(rec)) {if (fs_mgr_is_nonremovable(rec)) {LOG(WARNING) << "nonremovable no longer supported; ignoring volume";continue;}std::string sysPattern(rec->blk_device);std::string nickname(rec->label);int flags = 0;if (fs_mgr_is_encryptable(rec)) {flags |= android::vold::Disk::Flags::kAdoptable;*has_adoptable = true;}if (fs_mgr_is_noemulatedsd(rec)|| android::base::GetBoolProperty("vold.debug.default_primary", false)) {flags |= android::vold::Disk::Flags::kDefaultPrimary;}vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(new VolumeManager::DiskSource(sysPattern, nickname, flags)));}}return 0;
}

4.3、跟踪fs_mgr_read_fstab_default() 函数可以看到 解析的fstab 文件的路径是:"/etc/recovery.fstab"

-->system/core/fs_mgr/fs_mgr_fstab.cpp
struct fstab *fs_mgr_read_fstab_default()
{std::string default_fstab;// Use different fstab paths for normal boot and recovery boot, respectivelyif (access("/sbin/recovery", F_OK) == 0) {default_fstab = "/etc/recovery.fstab";} else {  // normal bootdefault_fstab = get_fstab_path();}...get_fstab_path 函数:static std::string get_fstab_path()
{for (const char* prop : {"hardware", "hardware.platform"}) {std::string hw;if (!fs_mgr_get_boot_config(prop, &hw)) continue;for (const char* prefix : {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab."}) {std::string fstab_path = prefix + hw;if (access(fstab_path.c_str(), F_OK) == 0) {return fstab_path;}}}return std::string();
}

4.4、接着回到解析函数 process_config,这里判断voldmanaged 属性

    if (fs_mgr_is_voldmanaged(rec)) {if (fs_mgr_is_nonremovable(rec)) {LOG(WARNING) << "nonremovable no longer supported; ignoring volume";continue;}std::string sysPattern(rec->blk_device);std::string nickname(rec->label);//这里加个log打印出fastab 文件每行的属性标签LOG(DEBUG) << "sysPattern="<<rec->blk_device<<",nickname="<<rec->label<<",mountPoint="<<rec->mount_point;int flags = 0;if (fs_mgr_is_encryptable(rec)) {flags |= android::vold::Disk::Flags::kAdoptable;*has_adoptable = true;}if (fs_mgr_is_noemulatedsd(rec)|| android::base::GetBoolProperty("vold.debug.default_primary", false)) {flags |= android::vold::Disk::Flags::kDefaultPrimary;}vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(new VolumeManager::DiskSource(sysPattern, nickname, flags)));}

打印的log如下,可以看到nickname 对应的fastab文件中的voldmanaged=usb就是设备类型了,这里是usb ,有的还有usbotg类型,vold里面会区分不同处理。

04-23 16:03:26.405  1582  1582 D vold    :sysPattern=/devices/platform/passthrough/5b0d0000.usb/ci_hdrc.0/*,nickname=usb,mountPoint=auto
04-23 16:03:26.405  1582  1582 D vold    : sysPattern=/devices/platform/5b0d0000.usb/ci_hdrc.0/*,nickname=usb,mountPoint=auto
04-23 16:03:26.405  1582  1582 D vold    : sysPattern=/devices/platform/5b110000.usb3/xhci-cdns3/usb1/1-1/1-1.1/*,nickname=usb,mountPoint=auto

解析完了之后接下来把每一条设备挂载信息作为DiskSource都通过addDiskSource添加到volumemanager 的list 数组中,接下来任何uevent事件都会在这里里面查询处理。

五、U盘插拔事件的流程分析

5.1、接下来以U盘插入和拔出事件为例分析下两次事件的流程。
vold和kernel 的通信是通过NetLink方式的,NetLink本质上使用的也是socket 的方式,这里 网络模块的netd 和kernel 通信亦是如此。

还记得前面main 函数里面NetLinkManager的初始化吗,这里面实际上是开启了一个socket 线程去监听kerne 传来的uevent 事件。调用流程如下:

NetlinkManager::start() -> NetlinkHandler.start()->SocketListener.startListener()
->pthread_create->runListener()

5.2、pthread_create中开启一个线程去循环监听socket 也就是kernel 的事件上报,最后给onDataAvailable 处理

void SocketListener::runListener() {SocketClientCollection pendingList;while(1) {SocketClientCollection::iterator it;fd_set read_fds;int rc = 0;int max = -1;FD_ZERO(&read_fds);if (mListen) {max = mSock;FD_SET(mSock, &read_fds);}FD_SET(mCtrlPipe[0], &read_fds);if (mCtrlPipe[0] > max)max = mCtrlPipe[0];pthread_mutex_lock(&mClientsLock);for (it = mClients->begin(); it != mClients->end(); ++it) {// NB: calling out to an other object with mClientsLock held (safe)int fd = (*it)->getSocket();FD_SET(fd, &read_fds);if (fd > max) {max = fd;}}pthread_mutex_unlock(&mClientsLock);SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {if (errno == EINTR)continue;SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);sleep(1);continue;} else if (!rc)continue;if (FD_ISSET(mCtrlPipe[0], &read_fds)) {char c = CtrlPipe_Shutdown;TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));if (c == CtrlPipe_Shutdown) {break;}continue;}if (mListen && FD_ISSET(mSock, &read_fds)) {int c = TEMP_FAILURE_RETRY(accept4(mSock, nullptr, nullptr, SOCK_CLOEXEC));if (c < 0) {SLOGE("accept failed (%s)", strerror(errno));sleep(1);continue;}pthread_mutex_lock(&mClientsLock);mClients->push_back(new SocketClient(c, true, mUseCmdNum));pthread_mutex_unlock(&mClientsLock);}/* Add all active clients to the pending list first */pendingList.clear();pthread_mutex_lock(&mClientsLock);for (it = mClients->begin(); it != mClients->end(); ++it) {SocketClient* c = *it;// NB: calling out to an other object with mClientsLock held (safe)int fd = c->getSocket();if (FD_ISSET(fd, &read_fds)) {pendingList.push_back(c);c->incRef();}}pthread_mutex_unlock(&mClientsLock);/* Process the pending list, since it is owned by the thread,* there is no need to lock it */while (!pendingList.empty()) {/* Pop the first item from the list */it = pendingList.begin();SocketClient* c = *it;pendingList.erase(it);/* Process it, if false is returned, remove from list */if (!onDataAvailable(c)) {release(c, false);}c->decRef();}}
}

5.3、SocketListener 的父类NetlinkListener 会重写onDataAvailable 处理uevent 事件,这里会封装成NetlinkEvent 对象,通过decode 方法解析完该事件的类型之后,调用NetlinkListener 的父类 NetlinkHandler onEvent 继续处理
bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
int socket = cli->getSocket();
ssize_t count;
uid_t uid = -1;

bool require_group = true;
if (mFormat == NETLINK_FORMAT_BINARY_UNICAST) {require_group = false;
}count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket,mBuffer, sizeof(mBuffer), require_group, &uid));
if (count < 0) {SLOGE("recvmsg failed (%s)", strerror(errno));return false;
}NetlinkEvent *evt = new NetlinkEvent();
if (evt->decode(mBuffer, count, mFormat)) {onEvent(evt);
} else if (mFormat != NETLINK_FORMAT_BINARY) {// Don't complain if parseBinaryNetlinkMessage returns false. That can// just mean that the buffer contained no messages we're interested in.SLOGE("Error decoding NetlinkEvent");
}delete evt;
return true;

}

5.4、NetlinkHandler 的onEvent 处理实际上是传递给VolumeManager 的 handleBlockEvent 处理,这里也说明了VolumeManager 的角色,不管什么事件都到它这里统一分发到各个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);}

5.5、VolumeManager 根据NetlinkEvent 的事件不同action 类型去做不同的处理,这里U盘插入事件action 值是1,U盘拔出 action 事件是2 。同时可以从NetlinkEvent 中获取设备类型和设备路径。主从设备号major minor 可以唯一标识一个设备,这里根据这个信息去区分是U盘设备还是sdcard 设备,进而设置flags 值。这里我们分析U盘插入事件handleDiskAdded

void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {std::lock_guard<std::mutex> lock(mLock);if (mDebug) {LOG(VERBOSE) << "----------------";LOG(VERBOSE) << "handleBlockEvent with action " << (int) evt->getAction();evt->dump();}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// emulator-specific; see Disk.cpp for details) devices are SD,// and that everything else is USBint flags = source->getFlags();if (major == kMajorBlockMmc|| (android::vold::IsRunningInEmulator()&& major >= (int) kMajorBlockExperimentalMin&& major <= (int) kMajorBlockExperimentalMax)) {flags |= android::vold::Disk::Flags::kSd;} else {flags |= android::vold::Disk::Flags::kUsb;}auto disk = new android::vold::Disk(eventPath, device,source->getNickname(), flags);LOG(DEBUG) << "VolumeManager::handleBlockEvent="<<eventPath<<", nickname="<<source->getNickname();handleDiskAdded(std::shared_ptr<android::vold::Disk>(disk));break;}}break;}case NetlinkEvent::Action::kChange: {LOG(DEBUG) << "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;}}
}

5.6、VolumeManager 的handleBlockEvent 在处理U盘插入事件时会创建一个disk 对象,通过该对象readMetadata 和readPartitions去读取U盘中 数据和分析信息。最后会创建PublicVolume 对象去处理U盘的 具体业务逻辑,包括和StorageManagerService 的通信。

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 itif (mSecureKeyguardShowing) {LOG(INFO) << "Found disk at " << disk->getEventPath()<< " but delaying scan due to secure keyguard";mPendingDisks.push_back(disk);} else {disk->create();mDisks.push_back(disk);}
}
-->system/vold/model/Disk.cpp
status_t Disk::create() {LOG(DEBUG) << "Disk::create";CHECK(!mCreated);mCreated = true;auto listener = VolumeManager::Instance()->getListener();if (listener) listener->onDiskCreated(getId(), mFlags);readMetadata();readPartitions();return OK;
}....
判断该U盘的文件系统格式是否支持,进入创建 PublicVolume
switch (type) {case 0x06:  // FAT16case 0x07:  // HPFS/NTFS/exFATcase 0x0b:  // W95 FAT32 (LBA)case 0x0c:  // W95 FAT32 (LBA)case 0x0e:  // W95 FAT16 (LBA)createPublicVolume(partDevice);break;
}

5.7、PublicVolume 就是具体处理U盘 挂载,卸载,格式化这些具体业务并且和framework 层的StorageManagerService 进行通信,这里是通过Bind跨进程实现的。之前做一个一个区分双U盘的问题。可以根据NetlinkEvent 时间的设备路径去进行区分,设备路径是唯一区分不同设备的方式。

六、结语

本篇博客到这里就结束了,下次画个时序图加上。后续会继续分析 framework 层的StorageManagerSevice 的处理逻辑。

Android 9.0 Vold 挂载流程分析相关推荐

  1. Android 7.0 Vold工作流程

    一.Vold工作机制 Vold是Volume Daemon的缩写,它是Android平台中外部存储系统的管控中心,是管理和控制Android平台外部存储设备的后台进程.其功能主要包括:SD卡的插拔事件 ...

  2. Android 7.0 WifiMonitor工作流程分析

    2019独角兽企业重金招聘Python工程师标准>>> 在wifi启动扫描的分析过程中,出现了多次WifiMonitor的操作,在此分析一下这个函数是如何工作的. 在Android的 ...

  3. android 6.0 vold shutdown流程

    这篇博客我们主要分析下vold在关机时候的流程,先看如下代码: 一.接收shutdown命令 这是vold接收MountService的命令,我们主要看下shutdown命令 int CommandL ...

  4. Android 8.0 Activity启动流程分析

    Activity启动过程中需要注意的一些类: Instrumentation     完成对Application和Activity初始化和生命周期调用的工具类.用来监控系统与应用的交互. Activ ...

  5. Android OTA升级原理和流程分析(五)---update.zip包从上层进入Recovery服务

    转载自:http://blog.chinaunix.net/uid-22028566-id-3533854.html 文章开头我们就提到update.zip包来源有两种: 一个是OTA在线下载(一般下 ...

  6. Android 11.0 Settings源码分析 - 主界面加载

    Android 11.0 Settings源码分析 - 主界面加载 本篇主要记录AndroidR Settings源码主界面加载流程,方便后续工作调试其流程. Settings代码路径: packag ...

  7. Android 8.0系统源码分析--Camera processCaptureResult结果回传源码分析

    相机,从上到下概览一下,真是太大了,上面的APP->Framework->CameraServer->CameraHAL,HAL进程中Pipeline.接各种算法的Node.再往下的 ...

  8. Android 7.1 屏幕旋转流程分析

    Android 7.1   屏幕旋转流程分析 一.概述 Android屏幕的旋转在framework主要涉及到三个类,结构如图 PhoneWindowManager:为屏幕的横竖屏转换的管理类. Wi ...

  9. Android Audio音量设置原理流程分析

    Android Audio音量设置原理流程分析 简介 本篇文章主要介绍Android音量设置从App应用层到framework层执行流程,以及相关的细节和原理分析,建议在阅读此文章前去看博主的混音理论 ...

最新文章

  1. 免费好用的Diff和Merge工具大总结
  2. 一个浏览器插件,测试xpath的工具
  3. CF1101A Minimum Integer 模拟
  4. 深入浅出之string
  5. mongo基本使用方法
  6. 简单的oracle备份恢复批处理文件 -- 转
  7. gcc编译以及Makefile与GDB调试
  8. 网络通信程序写起来很难专业课没问题
  9. C++ Lambda表达式demo
  10. [Ext JS 7]ClassRequire错误解决
  11. 用Kotlin撸一个图片压缩插件ImageSlimming-导学篇(一)
  12. 安装openguass数据库配套工具Data Studio
  13. 龙芯2F笔记本安装gentoo系统
  14. 计算机纸牌游戏攻略,Windows纸牌游戏怎么玩 玩法技巧攻略详解
  15. 运算放大器节点电压方程_比例运算放大器电路
  16. .Net core web api 上传图片代码 。 AutoMapper映射注入。sql suger数据库依赖注入
  17. 微信登陆失败redirect_uri 域名与后台配置不一致 10003(thinkphp)
  18. c fread 快读 详解_热量计算公式及例题详解
  19. 算法+剑指offerの刷题笔记
  20. Java输入三条边判断是否能组成三角形,若能构成则输出什么三角形

热门文章

  1. 早安心语/2017-6-28
  2. 支付宝内部人士是这样设密码的!太牛逼了!
  3. instr()函数的格式  (俗称:字符查找函数)
  4. C语言I———博客作业04
  5. linux测试磁盘读写能力
  6. 玩游戏不拖沓,续航也很强,QCY G1真无线电竞耳机体验
  7. 根号算法——暴力美学
  8. Vue专题(一)聊一聊双向绑定
  9. iCheck基本用法的使用
  10. 短网址是什么,短网址有哪些应用场景,如何使用长网址转短网址