文章目录
- 前言
- 框架
- MountService 流程
- Vold 流程
前言
印象中是参考 《深入理解 Android 卷 1 》 追的流程,差不多供参考吧
基于安卓 4.4
框架
MountService 流程
/*【初始化流程总结】:
SystemServerinitAndLoop()// 创建 MountService 服务对象mountService = new MountService(context);//MountService::MountService(Context context) ///// 从 xml 中读取存储设备列表,文件位于: frameworks/base/core/res/res/xml/storage_list.xmlreadStorageListLocked();///// 创建并启动一个带消息循环的 MountService 工作线程 HandlerThread hthread = new HandlerThread(TAG);hthread.start();// 为 MountService 工作线程创建一个 Handler // 有消息时时会调用 MountServiceHandler::handleMessage() 处理// 处理的消息类型为: H_UNMOUNT_PM_UPDATE,H_UNMOUNT_PM_DONE,H_UNMOUNT_MSmHandler = new MountServiceHandler(hthread.getLooper());///// Watch for user changes// 注册一系列变化广播接收器final IntentFilter userFilter = new IntentFilter();userFilter.addAction(Intent.ACTION_USER_ADDED);userFilter.addAction(Intent.ACTION_USER_REMOVED);userFilter.addAction(Intent.ACTION_USER_SWITCHED);mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);。。。//// Add OBB Action Handler to MountService thread.// 为 MountService 工作线程创建一个 ObbActionHandler mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());//// Create the connection to vold with a maximum queue of twice the// amount of containers we'd ever expect to have. This keeps an// "asec list" from blocking a thread repeatedly.//mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25);// 创建并启动一个 socket 连接监听线程Thread thread = new Thread(mConnector, VOLD_TAG);thread.start();// 会执行到传入参数的 run() 函数 ///NativeDaemonConnector::run()mCallbackHandler = new Handler(FgThread.get().getLooper(), this);while (true) {try {// 监听套接字 vold listenToSocket();} catch (Exception e) {loge("Error in NativeDaemonConnector: " + e);SystemClock.sleep(5000);}}/// Add ourself to the Watchdog monitors if enabled.if (WATCHDOG_ENABLE) {Watchdog.getInstance().addMonitor(this);}///// 将 mountService 服务注册到 C++ 层的 service manager 进程中管理 ServiceManager.addService("mount", mountService);【MountService 命令下发流程】:
MountService::mountVolume()// 核心函数int ret = doMountVolume(path); // 命令交给 NativeDaemonConnector::execute() 去发送mConnector.execute(cmd);//NativeDaemonConnector::execute()// 构造命令makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args);//// 向 socket 中写入命令mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8));【MountService 消息接收流程】:// 监听套接字是通过 NativeDaemonConnector 类实现的,它在 MountService 的构造函数中创建并初始化的
NativeDaemonConnector::run()mCallbackHandler = new Handler(FgThread.get().getLooper(), this);while (true) {try {// 监听套接字 vold listenToSocket();//创建 Vold socket socket = new LocalSocket();//向服务端发起连接请求 socket.connect(address);//从连接的 socket 中得到输入输出流 InputStream inputStream = socket.getInputStream();/// 对本次连接请求做一些回调处理, 连接成功回调处理mCallbacks.onDaemonConnected();////MountService::onDaemonConnected()/// 创建一个工作线程处理回调new Thread("MountService#onDaemonConnected"){run(){// 向vold查询所有的存储设备 final String[] vols = NativeDaemonEvent.filterMessageList(mConnector.executeForList("volume", "list"),VoldResponseCode.VolumeListResult);///判断存储设备状态 for (String volstr : vols) /// 更新 Volume 状态, 存储设备状态更新:updatePublicVolumeState(usbotgVolume, state);for (int i = mListeners.size() -1; i >= 0; i--) // 调用已注册的 MountServiceBinderListener 来通知存储设备状态改变 bl.mListener.onStorageStateChanged(path, oldState, state);///// 这里调用的是 StorageManager::MountServiceBinderListenerStorageManager::MountServiceBinderListener()for (int i = 0; i < size; i++)/// 私有类: 位于 StorageManager // ListenerDelegate 的 sendStorageStateChanged()mListeners.get(i).sendShareAvailabilityChanged(available);StorageManager::ListenerDelegate::sendStorageStateChanged() StorageStateChangedStorageEvent e = new StorageStateChangedStorageEvent(path, oldState, newState);// 给某个线程发送消息?mHandler.sendMessage(e.getMessage());//进入闭环数据读取模式 while (true) {int count = inputStream.read(buffer, start, BUFFER_SIZE - start);//当读取的数据长度小于0时,表示连接已断开,跳出循环,重新向服务端发起新的连接请求//解析读取到的数据,得到 NativeDaemonEvent for (int i = 0; i < count; i++)final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(rawEvent);//如果命令码code >= 600 && code < 700 if (event.isClassUnsolicited()) //将读取到的事件发送到 VoldConnector.CallbackHandler 线程中处理 mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage( event.getCode(), event.getRawEvent()));//////// NativeDaemonConnector 类中实现的NativeDaemonConnector::handleMessage()//回调 MountService 的 onEvent 函数进行处理mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))//// 回调处理接收到的消息,处理各种事件,主要是通知其他程序存储状态变化了// 并发送命令给 vold 进行挂卸载MountService::onEvent()if (code == VoldResponseCode.VolumeStateChange)。。。notifyVolumeStateChange(cooked[2], cooked[3], Integer.parseInt(cooked[7]),Integer.parseInt(cooked[10]), bValue);。。。 else if (code == VoldResponseCode.VolumeUuidChange) 。。。else if (code == VoldResponseCode.VolumeUserLabelChange)。。。else if ((code == VoldResponseCode.VolumeDiskInserted) ||(code == VoldResponseCode.VolumeDiskRemoved) ||(code == VoldResponseCode.VolumeBadRemoval))if (code == VoldResponseCode.VolumeDiskInserted) new Thread("MountService#VolumeDiskInserted"){public void run(){//// 进行实际挂载操作doMountVolume(path) if (!path.startsWith(EXTERNAL_OTG)) {doSDSwapVolumeUpdate();updateDefaultpath();sendSDSwapIntent(); } // 启动一个窗口?mContext.startActivity(intent); }}。。。else {//否则将改事件添加到响应队列中mResponseQueue.add(event.getCmdNumber(), event);}}}}.start();} catch (Exception e) {loge("Error in NativeDaemonConnector: " + e);SystemClock.sleep(5000);}}*/
Vold 流程
/* vold 进程是从 init 进程通过 init.rc 启动的 vold进程:管理和控制 Android 平台外部存储设备,包括 SD 插拨、挂载、卸载、格式化等;vold 进程接收来自内核的外部设备消息*/
service vold /system/bin/voldclass coresocket vold stream 0660 root mount // 语法:socket <name> <type> <perm> <user> <group>, 创建一个名字为 vold<name>,类别为 stream<type> //访问权限为 0660<perm> 用户为 root,用户组为 mountioprio be 2/*
涉及相关类的概念:
/// C++ /SocketClient // 代表一个套接字信息SocketListener // 创建线程,监听, socket,每个套接字会创建一个 SocketClient 链入链表管理// 【继承该类,会在接收到数据时,调用子类覆写的 onDataAvaible() 进行数据处理】NetlinkListener: public SocketListener // 监听内核 netlink, 接收 uevent 消息,封装到 NetlinkEvent,然后调// 用具体子类 onEvent() 处理NetlinkHandler: public NetlinkListener // 具体 netlink 消息命令的处理类,与 VolumeManager 交互,实现磁盘挂卸载等操作 FrameworkListener: public SocketListener // 用于监听远程发过来的字符串命令, 执行具体子类 runCommand() 来执行命令【CommandListener】: public FrameworkListener// 注册具体的命令子类,用来监听 /dev/socket/vold 上的数据发来的命令,// 该套接字用来与 Framework 通信,用注册的具体的命令子类,执行 Framework 发来的命令Volume // 分卷抽象【DirectVolume】:public Volume // 一个实体磁盘设备在代码中的抽象。NetlinkEvent // 解析 uevent 获得信息封装的类/// 接收内核发来的消息, 解析转换成 NetlinkEvent 对象;再将此 NetlinkEvent // 对象传递给 VolumeManager 处理, 他会传给具体的 DirectVolume 磁盘设备类处理【NetlinkManager】{// 实现单实例模式用NetlinkManagerNetlinkListener: public SocketListenerNetlinkHandler: public NetlinkListener SocketListener} /// 此模块管理所有挂载的设备节点以及相关操作执行【VolumeManager】{// 实例单例模式使用VolumeManager// 链表结构AsecIdCollectionVolumeCollectionSocketListener}
###########################################################################################################Java
// 检测外部存储卡的插入/拔出事件,这些事件是由 MountServie 通过
// Intent 广播发出的,例如,外部存储卡插入后,MountService 就会发送 ACTION_MEDIA_MOUNTED 消息
class MountService extends IMountService.Stub // 很眼熟,Binder 通信机制,Stub 是服务类需要继承实现的public MountService(Context context)mPms = (PackageManagerService) ServiceManager.getService("package");// 创建一个 HandlerThread HandlerThread hthread = new HandlerThread(TAG);hthread.start();// Handler 类: 用于让某个线程的消息队列发送消息让其处理 // 创建一个 Handler,这个 Handler 使用 HandlerThread 的 Looper, 也就是说,派发给该 Handler // 的消息将在另外一个线程中处理 mHandler = new MountServiceHandler(hthread.getLooper());// NativeDaemonConnector 用于 Socket 通信,第二个参数 vold 表示将和 vold 通信,也就是和 CommandListener 模块 // 中的那个 socket 建立通信连接,第二个参数为 INativeDaemonConnectorCallbacks 接口,它提供两个回调接口函数:// onDaemonConnected() // 当 NativeDaemonConnector 连接上 Vold 后回调 // onEvent() // 当 NativeDaemonConnector 收到来自 Vold 数据后回调 mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25);// 再启动一个线程用于和 Vold 通信 Thread thread = new Thread(mConnector, VOLD_TAG);thread.start();
###########################################################################################################
//
// 内核发来 uevent 消息举例:【SD 卡插入】
add@/devices/platform/msm_sdcc.2/mmc_host/mmc1/mmc1:c9f2/block/mmcblk0
ACTION=add // 设备插入,还有 remove/change 动作
DEVPATH=/devices/platform/msm_sdcc.2/mmc_host/mmc1/mmc1:c9f2/block/mmcblk0 // 该设备在 /sys 中的设备路径
SUBSYSTEM=block // 设备类型:block/character 等
MAJOR=179 // 主次设备号
MINOR=0
DEVNAME=mmcblk0
DEVTYPE=disk // 设备类型为磁盘
NPARTS=3 // 该 SD 卡有 3 个分区
SEQNUM=1357 // 序号 // SD 卡分区的 uevent
add@/devices/platform/msm_sdcc.2/mmc_host/mmc1/mmc1:c9f2/block/mmcblk0/mmcb1 k0p1
ACTION=add // 设备插入,还有 remove/change 动作
DEVPATH=/devices/platform/msm_sdcc.2/mmc_host/mmc1/mmc1:c9f2/block/mmcblk0/mmcblk0p1 // 该设备在 /sys 中的设备路径
SUBSYSTEM=block // 设备类型:block/character 等
MAJOR=179 // 主次设备号
MINOR=1
DEVNAME=mmcblk0p1
DEVTYPE=partition // 设备类型为分区
NPARTS=1 // 该 SD 卡有 3 个分区
SEQNUM=1358 // 序号 SocketListener 类具体实现/
// 监听 socket 的实现:调用具体子类 onDataAvaible() 处理
SocketListener 入口函数:static void *threadStart(void *obj);SocketListener::runListener() while(1) {// 添加请求监听套接字描述符FD_SET(mSock, &read_fds);// 监听管道读端FD_SET(mCtrlPipe[0], &read_fds);// 遍历所有链表上 SocketClient,这个数据结构里保存了监听的套字信息 for (it = mClients->begin(); it != mClients->end(); ++it) {int fd = (*it)->getSocket();// 添加数据通信的套接字描述符FD_SET(fd, &read_fds);if (fd > max)max = fd;}///// 通过 select 监听添加的文件描述符,看是否有数据送过来select(max + 1, &read_fds, NULL, NULL, NULL)// 如果管道上有数据传来的,则if (FD_ISSET(mCtrlPipe[0], &read_fds))break;// 如果连接请求监听套按字if (mListen && FD_ISSET(mSock, &read_fds))do {alen = sizeof(addr);// 接受连接请求c = accept(mSock, &addr, &alen);SLOGV("%s got %d from accept", mSocketName, c);} while (c < 0 && errno == EINTR);// 创建一个套接字客户端 SocketClient 去处理双方的数据通信// 这里仅将创建的客户端链入链表中管理mClients->push_back(new SocketClient(c, true, mUseCmdNum)); // 将所有有数据的客户端,添加到 pendingList 链表中pendingList->clear();pthread_mutex_lock(&mClientsLock);for (it = mClients->begin(); it != mClients->end(); ++it) {int fd = (*it)->getSocket();if (FD_ISSET(fd, &read_fds)) {pendingList->push_back(*it);}}// 遍历的所有有数据的客户端 while (!pendingList->empty()) {// 从链表中取出一项it = pendingList->begin();SocketClient* c = *it;pendingList->erase(it);///// 核心:调用 【onDataAvaible()】 函数处理数据,onDataAvaible() 是抽象函数,// 所以是动态绑定的,有则子类处理,无则父类处理if (!onDataAvailable(c) && mListen) {// onDataAvailable() 出错,则返回 false 从链表中删除for (it = mClients->begin(); it != mClients->end(); ++it) {if (*it == c) {mClients->erase(it);break;}}// Remove our reference to the client c->decRef();}}}// FrameworkListener 类具体实现 //
// 继承自 SocketListener,用于监听远程发过来的字符串命令, 执行自己实现的命令子类,调用具体子类的 runCommand()
FrameworkListener: public SocketListener继承 SocketListener, 主要覆写 onDataAvaible() 来处理数据onDataAvaible()// 调用 read() 来接收数据len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer+last_offset, sizeof(buffer)-last_offset))for (i = 0; i < len; i++) {if (buffer[i] == '\0') {// IMPORTANT: dispatchCommand() expects a zero-terminated string // 分发命令给具体的命令子类处理dispatchCommand(c, buffer + offset);// 解析发过来的字符串命令// 遍历已注册的命令集for (i = mCommands->begin(); i != mCommands->end(); ++i) {FrameworkCommand *c = *i;if (!strcmp(argv[0], c->getCommand())) {///// 调用具体的命令子类, 具体的命令子类需要继承 FrameworkCommand 类, 并需要注册进链表// 实现其 runCommand() 方法,这里添加了一些继承 VoldCommand,具体命令子类继承 VoldCommandif (c->runCommand(cli, argc, argv)) {SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));}goto out;}}offset = i + 1;}}//// 注册具体的继承 FrameworkCommand 的命令子类,仅是简单的添加到链表中void FrameworkListener::registerCmd(FrameworkCommand *cmd) {mCommands->push_back(cmd);}
// CommandListener 类具体实现 //
// 注册具体的命令子类,用来监听 /dev/socket/vold 上的数据发来的命令,
// 该套接字用来与 Framework 通信,用注册的具体的命令子类,执行 Framework 发来的命令
CommandListener : public FrameworkListener// 监听 /dev/socket/vold 并注册具体的命令子类 CommandListener::CommandListener() :FrameworkListener("vold", true) // 1. 注册监听 /dev/socket/vold 套接字,与 Framework 通信{// 2. 注册具体的命令子类,当从 Framework 层有命令来时,就会调用具体命令子类的 runCommandregisterCmd(new DumpCmd()); // 用于输出各种信息registerCmd(new VolumeCmd()); // 用于磁盘卷管理 registerCmd(new AsecCmd()); // 用于将 .apk 安装到外部存储设备如 SD 卡上 registerCmd(new ObbCmd()); // 不程春明二进制数据块,数据加密用registerCmd(new StorageCmd()); // 用于查询哪些进程正在使用某个磁盘设备registerCmd(new XwarpCmd());registerCmd(new CryptfsCmd()); // 存储区加密 registerCmd(new FstrimCmd());//M{#ifndef MTK_EMULATOR_SUPPORTregisterCmd(new USBCmd());#endifregisterCmd(new CDROMCmd());//}M#if defined (ENG_BUILD_ENG) registerCmd(new SilkRoad());#endif}/// NetlinkListener 类具体实现 /
// 继承自 SocketListener,用于监听内核发来的 uevent 事件,调用 NetlinkEvent 解析后
// 再调用 NetlinkListener 具体的子类 onEvent() 处理
NetlinkListener: public SocketListener继承 SocketListener, 主要覆写 onDataAvaible() 来处理数据onDataAvailable()// 接收来自内核的 uevent 消息count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv(socket, mBuffer, sizeof(mBuffer), &uid))// 创建一个 NetlinkEvent 对象解析处理消息NetlinkEvent *evt = new NetlinkEvent();// 调用 NetlinkEvent::decode() 解析命令 evt->decode(mBuffer, count, mFormat)NetlinkEvent::decode()// 根据命令类型是二进制的还是 ascii 类型的分别解析if (format == NetlinkListener::NETLINK_FORMAT_BINARY) {// Parse an binary message from a NETLINK_ROUTE netlink socket.return parseBinaryNetlinkMessage(buffer, size);} else {// 处理 uevent 消息如:add/remove/change 等 // 解析后的信息保存在 NetlinkEvent::{mAction/mSeq/mSubsystem/mParames} 中 return parseAsciiNetlinkMessage(buffer, size);}//// 虚函数,调用具体的继承了 NetlinkListener() 子类实现的 onEvent() 处理消息// 如会调用 NetlinkHandler:onEvent() 处理onEvent(evt);// NetlinkManager 类具体实现 //
// 接收内核发来的消息, 解析转换成 NetlinkEvent 对象;再将此 NetlinkEvent
// 对象传递给 VolumeManager 处理
【NetlinkManager】
{// 实现单实例模式用NetlinkManagerSocketListener # 会调用子类 onDataAvailable() 处理接收到的数据 NetlinkListener: public SocketListener # 调用子类 onEvent() 处理接收到的 uevent 事件 NetlinkHandler: public NetlinkListener // 实现的 onEvent()SocketListener
}【启用 NetLink 监听函数流程】:NetlinkManager::start()// 创建 netlink 用来监听内核消息 mSock = socket(PF_NETLINK, SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)// 绑定 netlinck 到文件描述符中 bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)// 创建一个 NetlinkHandler 类对象mHandler = new NetlinkHandler(mSock)// NetlinkHandler 继承 NetlinkListener 并实现了其 onEvent() 函数,会在 NetlinkManager::onDataAvailable()// 用来处理具体的 uevent 事件NetlinkHandler: public NetlinkListenermHandler->start()//NetlinkHandler::start()SocketListener::startListener()// android_get_control_socket - simple helper function to get the file// descriptor of our init-managed Unix domain socket. `name' is the name of the// socket, as given in init.rc. Returns -1 on error.//// This is inline and not in libcutils proper because we want to use this in// third-party daemons with minimal modification.// 从环境变量在获得已有的 socket 文件描述符, 这是在 init.rc 中注册的,用来与 framework 通信用的 /dev/socket/voldmSock = android_get_control_socket(mSocketName)// 监听套接字listen(mSock, 4)// 添加到 SocketListener 类的 mClients 链表中管理mClients->push_back(new SocketClient(mSock, false, mUseCmdNum)// 创建用于退出使用的管道pipe(mCtrlPipe)// 创建线程进行监听pthread_create(&mThread, NULL, SocketListener::threadStart, this)SocketListener::threadStart(void *obj)// 子类转为父类SocketListener *me = reinterpret_cast<SocketListener *>(obj);me->runListener();// 调用 SocketListener::runListener()// 具体参见上面 SocketListener 类具体实现,收到 socket 信息,则调用#####################################################// 【SocketListener 的子类 onDataAvaible() 进行处理】# 即 NetlinkListener::onDataAvaible()# 再调用 NetlinkListener 的子类覆写的 onEvent()# 即 NetlinkHandler::onEvent()#####################################################// 如果收到 pipe 管道信息,则退出线程【处理内核传来 uevent 数据流程】:// 在 NetlinkListener 中被调用,传入参数为解析好的 uevent 数据封装的 NetlinkEvent 类// 【然后调用 volumeManager 类进行处理 】NetlinkHandler::onEvent(NetlinkEvent *evt)// 单实例模式VolumeManager *vm = VolumeManager::Instance()// 获得是什么子系统上报的 uevent const char *subsys = evt->getSubsystem()if (!strcmp(subsys, "block")) // 调用 volumeManager 模块中的函数,进行具体的磁盘挂卸载操作// 【1】. 更新磁盘状态vm->updatePullOutState(evt);// 获得 uevent 事件类型: add/remove/change int action = evt->getAction();VolumeCollection::iterator it;// 遍历的所有已有分卷 Volume 类对象 updatePullOutState() 函数,更新各个分卷状态 for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {///// updatePullOutState() 是一个虚函数,所以会调用具体子类 DirectVolume 处理(*it)->updatePullOutState(evt); DirectVolume::updatePullOutState()// 获得要更新的设备路径及行为int action = evt->getAction();const char *dp = evt->findParam("DEVPATH"); // 遍历所有已经注册管理的磁盘的设备for (it = mPaths->begin(); it != mPaths->end(); ++it) if (!strncmp(dp, *it, strlen(*it)))if (action == NetlinkEvent::NlActionAdd)mIsHwPullOut = false;else if (action == NetlinkEvent::NlActionRemove)mIsHwPullOut = true;} // 【2】. 设置热插拔标志位vm->setHotPlug(true);mIsHotPlug = isPlug;// 【3】. 处理 uevent 事件: add/remove/change 设备 vm->handleBlockEvent(evt);// 遍历的所有已经注册管理的卷列表,卷列表根据配置文件 /etc/vold.fstab 创建添加// 在函数 process_config() 中创建添加的 for (it = mVolumes->begin(); it != mVolumes->end(); ++it)/// Volume::handleBlockEvent() 是一个虚函数,所以会调用具体子类 DirectVolume 处理// 调用 DirectVolume::handleBlockEvent()(*it)->handleBlockEvent(evt)DirectVolume::handleBlockEvent()// 获得磁盘变更路径const char *dp = evt->findParam("DEVPATH");// 遍历该设备的所有分区?for (it = mPaths->begin(); it != mPaths->end(); ++it) // 获得要执行的操作: add/remove/change 以及设备类型:disk/partition int action = evt->getAction();const char *devtype = evt->findParam("DEVTYPE");###########################// 添加设备 ###########################if (action == NetlinkEvent::NlActionAdd) // 获得要创建的设备节点主次设备号 int major = atoi(evt->findParam("MAJOR"));int minor = atoi(evt->findParam("MINOR"));// 要创建的节点路径snprintf(nodepath, sizeof(nodepath), "/dev/block/vold/%d:%d", major, minor);// 创建设备节点 createDeviceNode(nodepath, major, minor)Volume::createDeviceNode()mode_t mode = 0660 | S_IFBLK;dev_t dev = (major << 8) | minor;// 创建节点 mknod(path, mode, dev)///// 当前创建的节点代表一个磁盘 ///if (!strcmp(devtype, "disk"))handleDiskAdded(dp, evt)/DirectVolume::handleDiskAdded()mDiskMajor = atoi(evt->findParam("MAJOR"));mDiskMinor = atoi(evt->findParam("MINOR"));const char *tmp = evt->findParam("NPARTS");// 针对磁盘上的每个分区分别处理,将发送信息给 MountServicefor (int i = 0; i < MAX_PARTITIONS; i++)mPartMinors[i] = -1;// 没有分区if (mDiskNumParts == 0) setState(Volume::State_Idle);// 有分区 else setState(Volume::State_Pending)// 当前创建的节点代表一个分区 ,对于有分区的 SD 卡,会先收到 disk 消息,再收到 partition 分区消息elsehandlePartitionAdded(dp, evt);DirectVolume::handlePartitionAdded()int major = atoi(evt->findParam("MAJOR"));int minor = atoi(evt->findParam("MINOR"));const char *tmp = evt->findParam("PARTN");if (getState() != Volume::State_Formatting && (part_rescan_wait == WAIT_ON_NO_EVENT))setState(Volume::State_Idle);if(mVm->getIpoState() == VolumeManager::State_Ipo_Start)if (mRetryMount == true)mRetryMount = false;// 调用 Volume::mountVol() 执行挂载分区mountVol()Volume::mountVol()// 通过 CommandListener 给套接字 /dev/socket/vold 发送消息给 Framework 层 snprintf(errmsg, sizeof(errmsg), "Volume %s %s mount failed - no media",getLabel(), getFuseMountpoint());//// 调用 VolumeManager 中的 Broadcaster->CommandListener() 发送此 msg// 发送消息通知 Framework 层是在 SocketListener 中完成//// 这里工作的 SocketListener 是 VolumeManager 的,SocketListener 的派生类 CommandListener,// 用来与 Framework 交互的,监听 Socket 消息。通过 VolumeManager 中调用 sendBroadcast,// 与 CommandListener 模块进行交互//// 这里的调用关系为:// VolumeManger 模块调用 // CommandListener 模块函数 sendBroadcast() // 通过给套接字 /dev/socket/vold 发消息给 Framework 层 mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeMountFailedNoMedia,SocketListener::sendBroadcast()for (i = mClients->begin(); i != mClients->end(); ++i)(*i)->sendMsg(code, msg, addErrno, false)SocketListener::sendBroadcast()// 遍历所有已经注册管理的 SocketClient for (i = mClients->begin(); i != mClients->end(); ++i)(*i)->sendMsg(code, msg, addErrno, false)SocketClient::sendMsg()sendData(msg, strlen(msg) + 1) int rc = sendDataLocked(data, len); // 通过套接字发消息给 Framework 层?send(mSocket, p, brtw, MSG_NOSIGNAL); // 这里会对挂载标志进行处理 // 挂载分区,也是创建相应的设备节点 snprintf(nodepath,sizeof(nodepath), "/dev/block/vold/%d:%d",new_major, new_minor);// 通过 mknode 创建节点 createDeviceNode(nodepath, new_major, new_minor)// 更新 DirectVolume 设备状态 updateDeviceInfo(nodepath, new_major, new_minor);// 遍历的所有分区 for (i = 0; i < n; i++)#################################################################// 挂载 Fat 分区,这里只支持 Fat 分区 // 先将设备挂载到 /mnt/secure/staging 目录 Fat::doMount(devicePath, SEC_STGDIR, false, false, false,AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)// 将挂载点从 /mnt/secure/staging 移动 Framework 指定目录// 如 SD 卡则为:/mnt/sdcard doMoveMount(SEC_STGDIR, getMountpoint(), false)// 将存储卡上的 .android_secure() 目录挂载到 // /mnt/secure/asec 目录下,同时对 .android_secure// 进行一些特殊处理,这样没权限用户// 就无法更改破坏更改 .andoird_secure 目录的内容了mountAsecExternal(SEC_STGDIR)// 设置状态为 State_Mounted, 这个函数将发送信息给 MountServicesetState(Volume::State_Mounted, Fat::isFat32(fd))#################################################################if(part_rescan_wait == WAIT_ON_ADD_EVENT)// 发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行pthread_cond_signal(&part_rescan_cond);// 如果磁盘上所有分区都找到了,在上面的 setState() 函数中设备的状态的 if ((getState() == Volume::State_Idle) && (mVm->getIpoState() == VolumeManager::State_Ipo_Start))// 准备消息给 Framework 通知设备状态变化 snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)", getLabel(),getFuseMountpoint(), mDiskMajor, mDiskMinor);mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted, msg, false)###########################// 卸载设备,与挂载类似处理###########################else if (action == NetlinkEvent::NlActionRemove) if (!strcmp(devtype, "disk"))handleDiskRemoved(dp, evt);elsehandlePartitionRemoved(dp, evt);###########################// 改变设备,与挂载类似处理###########################else if (action == NetlinkEvent::NlActionChange) if (!strcmp(devtype, "disk"))handleDiskChanged(dp, evt);elsehandlePartitionChanged(dp, evt);// 【4】. 设置热插拔标志位 vm->setHotPlug(false);##################################################################################################################
main 流程总结:main() // 【1】创建 vold 设备文件夹 mkdir("/dev/block/vold", 0755);// Create our singleton managers // 【2】创建 VolumeManager 对象: 单实例模式 vm = VolumeManager::Instance())// 【3】创建 NetlinkManager 对象: 用于监听内核发来的 uevent 信息,然后交给 VolumeManager 处理nm = NetlinkManager::Instance())/// CommandListener 创建 vold socket 监听上层消息 // CommandListener: 监听来自 UNIX 本地套接字(/dev/socket/vold)上的数据// 继承自 FrameworkListener,可以用于分发字符串命令,处理字符串命令由子类// DumpCmd, VolumeCmd, AsecCmd, ObbCmd, StorageCmd, XwarpCmd 和 CryptfsCmd 等去完成 cl = new CommandListener();vm->setBroadcaster((SocketListener *) cl);nm->setBroadcaster((SocketListener *) cl);//// 【4】根据配置文件 /etc/vold.fstab 初始化 VolumeManager, 用来管理 DirectVolume(磁盘抽象对象)process_config(vm)// 解析 fstab 配置文件,保存到 fstab 数据结构中 fstab = fs_mgr_read_fstab(fstab_filename);// 遍历 fstab 配置文件中的所有选项,根据 fs_mgr_flags 文件管理标志判断是否需要将其添加到 volumeManager 管理for (i = 0; i < fstab->num_entries; i++)// 如果需要添加到 volumeManager 管理,则添加管理if (fs_mgr_is_voldmanaged(&fstab->recs[i]))// 创建 DirectVolume 对象 相关的挂载点设备的操作dv = new DirectVolume(vm, &(fstab->recs[i]), flags);//添加挂载点设备路径dv->addPath(fstab->recs[i].blk_device)//将 DirectVolume 添加到 VolumeManager 管理vm->addVolume(dv)/// 【5】启动 NetlinkManager socket 监听内核发送 uevent, 获得具体内核消息,它会将其封装成 NetlinkEvent 信息 // 然后调用 NetlinkHandler::onEvent() 【然后调用 volumeManager 类进行处理 】// 【这里是作服务器用的】nm->start()NetlinkManager::start()// 创建 PF_NETLINK 地址簇的 socket,目前只支持 SOCK_DGRAM 类型,// 第三个参数,NETLINK_KOBJECT_UEVENT 表示要接收内核的 Uevent 事件mSock = socket(PF_NETLINK, SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)// 设置 Socket 接收缓冲区大小setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz))// 必须对 socket 执行 bind 操作bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr))// 创建一个 NetlinkHandler 类对象监听处理内核 netlinck 消息// 它将根据接收的消息,调用具体的 VolumeManager 模块中的函数时行处理,如挂卸载 mHandler = new NetlinkHandler(mSock)//// 调用 NetlinkHandler::start() ,他会创建一个线程进行监听内核 netlink 发来的消息 // 消息处理流程为:// Kernel = uevent => NetlinkManagerModule ==> VolumeManager ==> DirectVolume(磁盘抽象类)//// 监听线程 // // 检查 socket 上是否有数据 // SocketListener # 调用子类覆写的 onDataAvaible() 进行数据处理// // 接收 socket 上的数据,将 uevent 数据解析封装成 NetlinkEvent, 调用 volumeManager 模块处理挂卸载// NetlinkListener::onDataAvaible() // NetlinkListener: public SocketListener, # 调用子类覆写的 onEvent() 进行数据处理// // 调用 volumeManager 模块处理挂卸载// NetlinkHandler::onEvent() // // VolumeManager 模块调用具体的磁盘 DirectVolume 类处理挂卸载 // VolumeManager::handleBlockEvent() // // 具体磁盘类 DirectVolume 处理挂卸载// DirectVolume::handleBlockEvent() // 1. 创建设备节点 // 2. 挂卸载文件系统 // 3. 发送套接字消息,通知 Framework 层 mHandler->start()////【6】向 /sys/block/ 目录下所有设备 uevent 文件写入 “add\n”,//触发内核 sysfs 发送 uevent 消息coldboot("/sys/block");DIR *d = opendir(path);do_coldboot(d, 0);dfd = dirfd(d);fd = openat(dfd, "uevent", O_WRONLY);write(fd, "add\n", 4);// 递归写所有子目录while((de = readdir(d))) fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);d2 = fdopendir(fd);do_coldboot(d2, lvl + 1);/// 【7】启动 CommandListener 用来监听 /dev/socket/vold 上的数据发来的命令,// 该套接字用来与 Framework 通信,用注册的具体的命令子类,执行 Framework 发来的命令cl->startListener()// 调用父类函数 SocketListener::startListener()// 上面有介绍,创建一个线程监听从 init.rc 中创建的 /dev/socket/vold 套接字,用来与 framework 通信 // 执行 Framework 发来的命令 /// 【8】死循环,成为监听线程// Eventually we'll become the monitoring threadwhile(1) {sleep(1000);}// 分析一下 SD 卡插入事件的检测:数据控制路径为:Kernel Uevent SocketListenerNetlinkListener::onDataAvaible()NetlinkHandler::onEvent() VolumeManager::handleBlockEvent() // 具体磁盘类 DirectVolume 处理挂卸载DirectVolume::handleBlockEvent() 1. 创建设备节点 2. 挂卸载文件系统 3. 发送套接字消息,通知 Framework 层 : 消息为: Volume sdcard disk inserted (179:0)======= Java =
SD 卡插入: Kernel 发出 uevent 消息add@/devices/platform/msm_sdcc.2/mmc_host/mmc1/mmc1:c9f2/block/mmcblk0ACTION=add // 设备插入,还有 remove/change 动作 DEVPATH=/devices/platform/msm_sdcc.2/mmc_host/mmc1/mmc1:c9f2/block/mmcblk0 // 该设备在 /sys 中的设备路径SUBSYSTEM=block // 设备类型:block/character 等MAJOR=179 // 主次设备号 MINOR=0 DEVNAME=mmcblk0 DEVTYPE=disk // 设备类型为磁盘 NPARTS=3 // 该 SD 卡有 3 个分区 SEQNUM=1357 // 序号 // Vold //// 检查 socket 上是否有数据 SocketListener # 调用子类覆写的 onDataAvaible()// 接收 socket 上的数据,将 uevent 数据解析封装成 NetlinkEvent, 调用 volumeManager 模块处理挂卸载NetlinkListener::onDataAvaible() // NetlinkListener: public SocketListener # 调用子类覆写的 onEvent()// 调用 volumeManager 模块处理挂卸载NetlinkHandler::onEvent() // VolumeManager 模块调用具体的磁盘 DirectVolume 类处理挂卸载 VolumeManager::handleBlockEvent() // 具体磁盘类 DirectVolume 处理挂卸载DirectVolume::handleBlockEvent() 1. 创建设备节点 2. 挂卸载文件系统 3. 发送套接字消息,通知 Framework 层 : 消息为: Volume sdcard disk inserted (179:0)// Java MountService::onEvent() if (code == VoldResponseCode.VolumeStateChange)...else if (code == VoldResponseCode.VolumeUuidChange) ......else if ((code == VoldResponseCode.VolumeDiskInserted) ||(code == VoldResponseCode.VolumeDiskRemoved) ||(code == VoldResponseCode.VolumeBadRemoval))// 磁盘设备插入 if (code == VoldResponseCode.VolumeDiskInserted)// 收到 handleDiskAdded() 发送的 VolumeDiskInserted 消息了 // 单独启动一个线程来处理这个消息 new Thread("MountService#VolumeDiskInserted"){public void run()// 调用 doMountVolume 处理 rc = doMountVolume(path)// 【参考 Android 2.2】,在 4.4 上未找通,太晚了,不找了// 通过 NativeDaemonConnector 给 Vold 发送请求mConnector.doCommand()/// C++ //// CommandListener 从 /dev/socket/vold 中接收到这个请求 // 命令内容为:volume mount /mnt/sdcard // 调用具体的命令子类执行 CommandListener::VolumeCmd::runCommand()VolumeManager *vm = VolumeManager::Instance();。。。 else if (!strcmp(argv[1], "mount"))// 调用 VolumeManager 模块的 mountVolume() 来处理 mount 命令 // 参数为 /mnt/sdcard rc = vm->mountVolume(argv[2]);VolumeManager::mountVolume()Volume *v = lookupVolume(label);v->mountVol()Volume::mountVol()// 最终调用具体文件系统的挂载函数,实现挂载Fat::doMount()}.start()*/