Android的存储系统—Vold与MountService分析

本文详细解析了Android存储系统的工作原理,重点介绍了Vold与MountService的交互过程,以及SD卡挂载的整个流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android的存储系统—Vold与MountService分析(一)

Android的存储系统(一)

看了很长时间Vold存储模块的相关知识,也死扣了一段时间的Android源码,发现Android存储系统所涉及的函数调用,以及Kernel与上层之间的Socket传输真的是让人头疼,除了需要整理整个架构的原理以外,还要反复看源码,真真的郁闷。

郁闷之余,还是打算把自己看过的经验之贴和参考资料进行整理,以帖子的形式发出来,供码神们参考,有不对的地方请指正,我们互相交流,下面就进入主题。

Android的存储系统主要由:SystemServer进程中的MountService和Vold进程中的VolumeManager组成。

它们管理着系统的存储设备,执行各种操作,如:mount、unmount、format等。

图1 Android存储系统架构图

图2 Android存储系统原理图

【重要组成分析】

1、NetlinkManager

     全称是NetlinkManager.cpp位于Android 4.x 源码位置/system/vold/NetlinkManager.cpp。

     该类的主要通过引用NetlinkHandler类中的onEvent()方法来接收来自内核的事件消息,NetlinkHandler位于/system/vold/NetlinkHandler.cpp。

2、VolumeManager

     全称是VolumeManager.cpp位于Android 4.x源码位置/system/vold/VolumeManager.cpp。该类的主要作用是接收经过NetlinkManager处理过后的事件消息。

     因为我们这里是SD的挂载,因此经过NetlinkManager处理过后的消息会分为五种,分别是:block、switch、usb_composite、battery、power_supply。

     这里SD卡挂载的事件是block。

3、DirectVolume

     位于/system/vold/DirectVolume.cpp。该类的是一个工具类,主要负责对传入的事件进行进一步的处理。

     block事件又可以分为:Add、Removed、Change、Noaction这四种。

4、Volume

     位于/system/vold/Volume.cpp,该类是负责SD卡挂载的主要类。Volume.cpp主要负责检查SD卡格式,以及对复合要求的SD卡进行挂载,并通过Socket将消息SD卡挂载的消息传递给NativeDaemonConnector。

5、CommandListener

     该类位于位于/system/vold/CommandListener.cpp,通过vold socket与NativeDaemonConnector通信。

6、NativeDaemonConnector

     该类位于frameworks/base/services/java/com.android.server/NativeDaemonConnector.java。该类用于接收来自Volume.cpp 发来的SD卡挂载消息并向上传递。

7、MountService

     位于frameworks/base/services/java/com.android.server/MountService.java。

     MountService是一个服务类,该服务是系统服务,提供对外部存储设备的管理、查询等。在外部存储设备状态发生变化的时候,该类会发出相应的通知给上层应用。在Android系统中这是一个非常重要的类。

8、StorageManaer

     位于frameworks/base/core/java/andriod/os/storage/StorageManager.java。

     在该类的说明中有提到,该类是系统存储服务的接口。在系统设置中,有Storage相关项,同时Setting也注册了该类的监听器。

     而StorageManager又将自己的监听器注册到了MountService中,因此该类主要用于上层应用获取SD卡状态。

 

【SD卡挂载流程】

1、Kernel发出SD卡插入uevent消息。

2、NetlinkHandler::onEvent()接收内核发出的uevent并进行解析。

3、VolumeManager::handleBlockEvent()处理经过第二步处理后的事件。

4、接下来调用DirectVolume::handleBlockEvent()。

在该方法中需要注意亮点:

(1)程序首先会遍历mPath容器,寻找与event对应的sysfs_path是否存在于mPath容器中;

(2)针对event中的action有4种处理方式:Add、Removed、Change、Noaction。

 

 

 

 

5、经过上一步之后会调用DirectVolume::handleDiskAdded()方法,该方法中会广播disk insert消息。

6、SocketListener::runListener()会接收DirectVolume::handleDiskAdded()广播的消息。该方法主要完成对event中数据的获取,通过Socket。

7、调用FrameworkListener::onDataAvailable()方法处理接收到的消息内容。

8、FrameworkListener::dispatchCommand()该方法用于分发指令。

9、在FrameworkListener::dispatchCommand()方法中,通过runCommand()方法去调用相应的指令。

10、在/system/vold/CommandListener.cpp中有runCommand()的具体实现。在该类中可以找到这个方法:CommandListener::VolumeCmd::runCommand(),从字面意思上来看这个方法就是对Volume分发指令的解析。该方法中会执行“mount”函数:vm>mountVolume(arg[2])。

11、mountVolume(arg[2])在VolumeManager::mountVolume()中实现,在该方法中调用v>mountVol()。

12、mountVol()方法在Volume::mountVol()中实现,该函数是真正的挂载函数。(在该方法中,后续的处理都在该方法中,在Mount过程中会广播相应的消息给上层,通过setState()函数)。

13、setState(Volume::Checking);广播给上层,正在检查SD卡,为挂载做准备。

14、Fat::check();SD卡检查方法,检查SD卡是否是FAT格式。

15、Fat::doMount()挂载SD卡。

至此,SD的挂载已算初步完成,接下来应该将SD卡挂载后的消息发送给上层,在13中也提到过,在挂载以及检查的过程中其实也有发送消息给上层的。

 

16、MountService的构造函数中会开启监听线程,用于监听来自vold的socket信息。

     Thread thread = new Thread(mConnector,VOLD_TAG); thread.start();

17、mConnector是NativeDaemonConnector的对象,NativeDaemonConnector继承了Runnable并Override了run方法。在run方法中通过一个while(true)调用ListenToSocket()方法来实现实时监听。

18、在ListenToSocket()中,首先建立与Vold通信的Socket Server端,然后调用MountService中的onDaemonConnected()方法。

19、onDaemonConnected()方法是在接口INativeDaemonConnectorCallbacks中定义的,MountService实现了该接口并Override了onDaemonConnected()方法。该方法开启一个线程用于更新外置存储设备的状态,主要更新状态的方法也在其中实现。

20、然后回到ListenToSocket中,通过inputStream来获取Vold传递来的event,并存放在队列中。

21、然后这些event会在onDaemonConnected()通过队列的”队列.take()”方法取出。并根据不同的event调用updatePublicVolumeState()方法,在该方法中调用packageManagerService中的updateExteralState()方法来更新存储设备的状态。

22、更新是通过packageHelper.getMountService().finishMediaUpdate()方法来实现的。

23、在updatePublicVolumeState()方法中,更新后会执行如下代码:

       bl.mListener.onStorageStateChanged();

在Android源码/packages/apps/Settings/src/com.android.settings.deviceinfo/Memory.java代码中,实现了StorageEventListener 的匿名内部类,并Override了onStorageStateChanged()方法。因此在updatePublicVolumeState()中调用onStorageStateChanged()方法后,Memory.java中也会收到。在Memory.java中收到以后会在Setting界面进行更新,系统设置—存储中会更新SD卡的状态。从而SD卡的挂载从底层到达了上层。

 

在下一个帖子中我会对Vold模块的源码以及MountService服务进行分析,包括main函数、NetlinkManager、NetlinkHandler、处理block类型的uevent、处理MountService命令、VolumeManager、NativeDaemonConnector等源码,很快就会与大家见面,感谢支持,欢迎交流与指正!

 

Android的存储系统—Vold与MountService分析(二)

Android的存储系统(二)

回顾:前贴主要分析了Android存储系统的架构和原理图,简要的介绍了整个从Kernel-->Vold-->上层MountService之间的数据传输流程,在这样的基础上,我们开始今天的源码分析!

【源码分析】

1. Vold的main函数

  Vold也是通过init进程启动,它在init.rc中的定义如下:

1 service vold /system/bin/vold
2     class core
3     socket vold stream 0660 root mount
4     ioprio be 2

  Vold服务放到了core分组,这就意味着系统启动时,它就会被init进程启动。这里定义的一个socket,主要用语Vold和Java层的MountService通信

   Vold模块的源代码位于system/vold,我们看看入口函数main(),代码如下:

复制代码
1 int main() {
2     VolumeManager *vm;
3     CommandListener *cl;
4     NetlinkManager *nm;
5
6     SLOGI("Vold 2.1 (the revenge) firing up");
7     
8     mkdir("/dev/block/vold", 0755);                 // 创建vold目录
9
10    klog_set_level(6);
11
12    if (!(vm = VolumeManager::Instance())) {        // 创建VolumeManager对象
13        exit(1);
14    };
15     
16    if (!(nm = NetlinkManager::Instance())) {       // 创建NetlinkManager对象
17        exit(1);
18    };
19
22    cl = new CommandListener();                     // 创建CommandListener对象
23    vm->setBroadcaster((SocketListener *) cl);      // 建立vm和cl的联系
24    nm->setBroadcaster((SocketListener *) cl);      // 建立nm和cl的联系
25
26    if (vm->start()) {                              // 启动VolumeManager
27        exit(1);
28    }
29
30    if (process_config(vm)) {                       // 创建文件/fstab.xxx中定义的Volume对象
31        SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));
32    }
33
34    cryptfs_pfe_boot();
35
36    if (nm->start()) {                              // 启动NetlinkManager,会调用NetlinkManager的start()方法,它创建PF_NETLINK socket,并开启线程从此socket中读取数据
37        exit(1);
38    }
39
40    coldboot("/sys/block");                         // 冷启动,创建/sys/block下的节点文件
41
42    if (cl->startListener()) {                      // 开始监听Framework的socket
43        exit(1);
44    }
45    
46    while(1) {                                      // 进入循环
47        sleep(1000);                                // 主线程进入休眠
48    }
49
50 SLOGI(
"Vold exiting"); 51 exit(0); 52 }
复制代码

   main函数的主要工作是创建3个对象:VolumeManager、NetlinkManager和CommandListener,同时将CommandListener对象分别设置到了VolumeManager对象和NetlinkManager对象中。

  从前贴的架构图中可以发现,CommandListener对象用于和Java层的NativeDaemonConnector对象进行socket通信,因此,无论是VolumeManager对象还是NetlinkManager对象都需要拥有CommandListener对象的引用。

2. 监听驱动发出的消息—Vold的NetlinkManager对象

  NetlinkManager对象的主要工作是监听驱动发出的uevent消息。

  main()函数中调用NetlinkManager类的静态函数Instance()来创建NetlinkManager对象,代码如下:

1 NetlinkManager *NetlinkManager::Instance() {
2       if (!sInstance)
3           sInstance = new NetlinkManager();      // NetlinkManager对象通过静态变量sInstance来引用,这意味着vold进程中只有一个NetlinkManager对象。
4       return sInstance;
5

  看下NetlinkManager的构造函数,代码如下:

1 NetlinkManager::NetlinkManager() {
2       mBroadcaster = NULL;
3

  NetlinkManager的构造函数只是对mBroadcaster进行了初始化。我们可以发现main()函数中通过调用NetlinkManager的setBroadcaster()函数来给变量mBroadcaster重新赋值。

nm->setBroadcaster((SocketListener *) cl);

  main()函数还调用了NetlinkManager的start()函数,我们观察一下NetlinkManager中的start()方法,代码如下:

复制代码
 1 int NetlinkManager::start() {
 2      struct sockaddr_nl nladdr;
 3      int sz = 64 * 1024;
 4      int on = 1;
 5 
 6      memset(&nladdr, 0, sizeof(nladdr));
 7      nladdr.nl_family = AF_NETLINK;
 8      nladdr.nl_pid = getpid();
 9      nladdr.nl_groups = 0xffffffff;
10      /*创建一个socket用于内核空间和用户空间的异步通信,监控系统的hotplug事件*/
11      if ((mSock = socket(PF_NETLINK,SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
12          SLOGE("Unable to create uevent socket: %s", strerror(errno));
13          return -1;
14      }
15      /*设置缓冲区大小为64KB*/
16      if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
17          SLOGE("Unable to set uevent socket SO_RCVBUFFORCE option: %s", strerror(errno));
18          goto out;
19      }
20      /*设置允许 SCM_CREDENTIALS 控制消息的接收*/
21      if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
22          SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));
23          goto out;
24      }
25      /*绑定 socket 地址*/
26      if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
27          SLOGE("Unable to bind uevent socket: %s", strerror(errno));
28          goto out;
29      }
30      /*利用新创建的socket实例化一个NetlinkHandler类对象用于监听socket,NetlinkHandler继承了类NetlinkListener,NetlinkListener又继承了类SocketListener*/
31      mHandler = new NetlinkHandler(mSock);
32       if (mHandler->start()) {                                               // 启动NetlinkHandler,调用NetlinkHandler的start()函数
33           SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
34           goto out;
35      }
36 
37      return 0;
38 
39 out:
40      close(mSock);
41      return -1;
42 }
复制代码

   我们看一下NetlinkManager的家族关系,如下图:

  上面的虚线为启动时的调用流程:

  (1) class NetlinkManager(在其start函数中创建了NetlinkHandler对象,并把创建的socket作为参数)

  (2)class NetlinkHandler: public NetlinkListener(实现了onEvent)

  (3) class NetlinkListener : public SocketListener(实现了onDataAvailable)

  (4) class SocketListener(实现了runListener,在一个线程中通过select查看哪些socket有数据,通过调用onDataAvailable来读取数据)。

总结:此贴主要分析了Vold的main()函数和NetlinkManager对象的源码,通过源码了解对象的创建时机和函数调用流程,下一贴会继续从NetlinkHandler的start()方法深入分析,继续源码的学习,很快会与大家见面,欢迎大家批评指正,我们互相学习。

Android的存储系统—Vold与MountService分析(三)

Android的存储系统(三)

回顾:前帖分析了Vold的main()函数和NetlinkManager的函数调用流程,截止到NetlinkHandler的创建和start()调用,本帖继续分析源码

 

 

1、处理block类型的uevent

  main()函数创建了CommandListener对象,NetlinkManager的start()函数又创建了NetlinkHandler对象,如果将CommandListener类和NetlinkHandler类的继承关系图画出来,会发现它们都是从SocketListener类派生出来的,如下图所示:

 

图1 NetlinkHandler和CommandListener的继承关系

  原理:处于最底层的SocketListener类的作用是监听socket的数据,接收到数据后分别交给FrameworkListener类和NetlinkListener类的函数,并分别对来自Framework和驱动的数据进行分析,分析后根据命令再分别调用CommandListener和NetlinkHandler中的函数。

  观察NetlinkHandler类的构造方法,代码如下:

NetlinkHandler::NetlinkHandler(int listenerSocket) :
                NetlinkListener(listenerSocket) {
}

  这个构造方法很简单,再看看它的start()方法,代码如下:

int NetlinkHandler::start() {
    return this->startListener();
}

  可以发现,start()方法调用了SocketListener的startListener()函数,代码如下:

复制代码
 1 int SocketListener::startListener(int backlog) {
 2      if (!mSocketName && mSock == -1) {
 3          SLOGE("Failed to start unbound listener");
 4          errno = EINVAL;
 5          return -1;
 6      } else if (mSocketName) {            // 只有CommandListener中会设置mSocketName
 7            if ((mSock = android_get_control_socket(mSocketName)) < 0) {
 8                SLOGE("Obtaining file descriptor socket '%s' failed: %s",mSocketName, strerror(errno));
 9                return -1;
10            }
11            SLOGV("got mSock = %d for %s", mSock, mSocketName);
12      }
13 
14      if (mListen && listen(mSock, backlog) < 0) {
15          SLOGE("Unable to listen on socket (%s)", strerror(errno));
16          return -1;
17      } else if (!mListen)
18           mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));
19 
20      if (pipe(mCtrlPipe)) {                          // 创建管道,用于退出监听线程
21          SLOGE("pipe failed (%s)", strerror(errno));
22          return -1;
23      }
24 
25      if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {         // 创建一个监听线程
26          SLOGE("pthread_create (%s)", strerror(errno));
27          return -1;
28      }
29 
30      return 0;
31 }
复制代码

  startListener()函数开始监听socket,这个函数在NetlinkHandler中会被调用,在CommandListener也会被调用。

  startListener()函数首先判断变量mSocketName是否有值,只有CommandListener对象会对这个变量赋值,它的值就是在init.rc中定义的socket字符串。

      调用函数 android_get_control_socket()的目的是从环境变量中取得socket的值,这样CommandListener对象得到了它需要监听的socket,

  而对于NetlinkHandler对象而言,它的mSocket不为NULL,前面已经创建了socket。

 

  startListener()函数接下来会根据成员变量mListener的值来判断是否需要调用Listen()函数来监听socket。这个mListen的值在对象构造时根据参数来初始化。

  对于CommandListener对象,mListener的值为ture,对于NetlinkHandler对象,mListener的值为false,这是因为CommandListener对象和SystemServer通信,需要监听socket连接,而NetlinkHandler对象则不用。

  

  接下来startListener()函数会创建一个管道,这个管道的作用是通知线程停止监听,这个线程就是startListener()函数最后创建的监听线程,它的运行函数是threadStart(),在前贴的NetlinkManager家族图系中我们可以清晰的发现,其代码如下:

void *SocketListener::threadStart(void *obj) {
     SocketListener *me = reinterpret_cast<SocketListener *>(obj);
     me->runListener();                                             // 调用runListener()方法
     pthread_exit(NULL);
     return NULL;
}

  threadStart()中又调用了runListener()函数,代码如下:

复制代码
 1 void SocketListener::runListener() {
 2 
 3       SocketClientCollection pendingList;
 4 
 5       while(1) {             // 无限循环,一直监听
 6             SocketClientCollection::iterator it;
 7             fd_set read_fds;
 8             int rc = 0;
 9             int max = -1;
10 
11             FD_ZERO(&read_fds);       // 清空文件描述符集read_fds
12 
13             if (mListen) {            // 如果需要监听
14                 max = mSock;
15                 FD_SET(mSock, &read_fds);               // 把mSock加入到read_fds
16             }
17 
18             FD_SET(mCtrlPipe[0], &read_fds);                 // 把管道mCtrlPipe[0]也加入到read_fds
19             if (mCtrlPipe[0] > max)
20                 max = mCtrlPipe[0];
21 
22             pthread_mutex_lock(&mClientsLock);               // 对容器mClients的操作需要加锁
23             for (it = mClients->begin(); it != mClients->end(); ++it) {    // mClient中保存的是NetlinkHandler对象的socket,或者CommandListener接入的socket
24                   int fd = (*it)->getSocket();
25                   FD_SET(fd, &read_fds);      // 遍历容器mClients的所有成员,调用内联函数getSocket()获取文件描述符,并添加到文件描述符集read_fds
26                   if (fd > max) {                                     // 也加入到read_fds
27                       max = fd;
28                   }
29             }
30             pthread_mutex_unlock(&mClientsLock);
31             SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);
32             if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {              // 执行select调用,开始等待socket上的数据到来
33                  if (errno == EINTR)                                              // 因为中断退出select,继续
34                      continue;
35                  SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);
36                  sleep(1);                               // select出错,休眠1秒后继续
37                  continue;
38             } else if (!rc)
39                  continue;                           // 如果fd上没有数据到达,继续
40 
41             if (FD_ISSET(mCtrlPipe[0], &read_fds)) {
42                 char c = CtrlPipe_Shutdown;
43                 TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));
44                 if (c == CtrlPipe_Shutdown) {
45                     break;
46                 }
47                 continue;
48             }
49             if (mListen && FD_ISSET(mSock, &read_fds)) {           // 如果是CommandListener对象上有连接请求
50                 struct sockaddr addr;
51                 socklen_t alen;
52                 int c;
53 
54                 do {
55                       alen = sizeof(addr);
56                       c = accept(mSock, &addr, &alen);             // 接入连接请求
57                       SLOGV("%s got %d from accept", mSocketName, c);
58                 } while (c < 0 && errno == EINTR);                 // 如果是中断导致失败,重新接入
59                 if (c < 0) {
60                     SLOGE("accept failed (%s)", strerror(errno));
61                     sleep(1);
62                     continue;                                      // 接入发生错误,继续循环
63                 }
64                 pthread_mutex_lock(&mClientsLock);
65                 mClients->push_back(new SocketClient(c, true, mUseCmdNum));   // 把接入的socket连接加入到mClients,这样再循环时就会监听到它的数据到达
66                 pthread_mutex_unlock(&mClientsLock);
67             }
68             
69             /* Add all active clients to the pending list first */
70             pendingList.clear();
71             pthread_mutex_lock(&mClientsLock);
72             for (it = mClients->begin(); it != mClients->end(); ++it) {
73                  SocketClient* c = *it;
74                  int fd = c->getSocket();
75                  if (FD_ISSET(fd, &read_fds)) {
76                      pendingList.push_back(c);             // 如果mClients中的某个socket上有数据了,把它加入到pendingList列表中
77                      c->incRef();
78                  }
79             }
80             pthread_mutex_unlock(&mClientsLock);
81 
82             /* Process the pending list, since it is owned by the thread,* there is no need to lock it */
83             while (!pendingList.empty()) {                 // 处理pendingList列表
84                   /* Pop the first item from the list */
85                   it = pendingList.begin();
86                   SocketClient* c = *it;
87                   pendingList.erase(it);                   // 把处理了的socket从pendingList列表中删除
88                   /* Process it, if false is returned, remove from list */
89                   if (!onDataAvailable(c)) {
90                       release(c, false);   // 调用release()函数-->调用onDataAvailable()方法
91                   }
92                   c->decRef();
93             }
94       }
95 }
复制代码

  SocketListener::runListener是线程真正执行的函数。

  以上runListener()函数虽然比较长,但这是一段标准的处理混合socket连接的代码,对于我们编写socket的程序大有帮助,这里先做简单了解。

 

  <--------接下来,我们继续分析......-------->

 

  runListener()函数收到从驱动传递的数据或者MountService传递的数据后,调用onDataAvailable()函数来处理,FrameworkListener类和NetlinkListener类都会重载这个函数。

  首先来分析一下NetlinkListener类的onDataAvailable()函数是如何实现的!

  直接上代码:

复制代码
 1 bool NetlinkListener::onDataAvailable(SocketClient *cli)
 2 {
 3       int socket = cli->getSocket();
 4       ssize_t count;
 5       uid_t uid = -1;
 6       /*从socket中读取kernel发送来的uevent消息*/
 7       count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv(socket, mBuffer, sizeof(mBuffer), &uid));
 8       if (count < 0) {                     // 如果count<0,进行错误处理
 9           if (uid > 0)
10               LOG_EVENT_INT(65537, uid);
11           return false;
12       }
13 
14       NetlinkEvent *evt = new NetlinkEvent();     // 创建NetlinkEvent对象
15       if (evt->decode(mBuffer, count, mFormat)) {     // 调用decode()函数
16           onEvent(evt);                           // 在NetlinkHandler中实现17       } else if (mFormat != NETLINK_FORMAT_BINARY) {
18           SLOGE("Error decoding NetlinkEvent");
19       }
20       delete evt;
21       return true;
22 }
复制代码

  NetlinkListener类的onDataAvailable()函数首先调用uevent_kernel_multicast_uid_recv()函数来接收uevent消息。

  接收到消息后,会创建NetlinkEvent对象,然后调用它的decode()函数对消息进行解码,然后用得到的消息数据给NetlinkEvent对象的成员变量赋值。

  最后onDataAvailable()函数调用了onEvent()函数继续处理消息,onEvent()函数的代码如下:

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

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

      if (!strcmp(subsys, "block")) {
          vm->handleBlockEvent(evt);           // 调用VolumeManager的handleBlockEvent()函数来处理
      }
}
复制代码

  NetlinkHandler的onEvent()函数中会判断event属于哪个子系统的,如果属于“block”(SD热插拔),则调用VolumeManager的handleBlockEvent()函数来处理,代码如下:

复制代码
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
      const char *devpath = evt->findParam("DEVPATH");
      VolumeCollection::iterator it;
      bool hit = false;
      for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
           if (!(*it)->handleBlockEvent(evt)) {        // 对每个DirectVolume对象,调用它handleBlockEvent来处理这个event
               hit = true;                             // 如果某个Volume对象处理了Event,则返回
               break;
           }
      }
.....
}
复制代码

总结:本帖的源码分析先到这里为止,下一贴再分析DirectVolume对象的handleBlockEvent()函数以及CommandListener对象如何处理从MountService发送的命令数据,即我们之前还没有讨论的关于FrameworkListener的onDataAvailable()函数的代码!

PS:希望对Android手机开发、IOS、以及游戏(纯兴趣,白菜)和Java EE感兴趣的码友们互粉,这样我也能及时的看到你们的大神之作和经验之贴,感谢感谢!



原文地址 http://www.cnblogs.com/pepsimaxin/p/5195842.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值