1 总体介绍

  在Android 中,当SD卡插入系统之后,系统会自动挂载。Vold 就是负责挂载SD卡的,vold 的全称是volume daemon。实际上是负责完成系统的CDROM,USB 大容量存储,MMC 卡等扩展存储的挂载任务自动完成的守护进程。它提供的主要特点是支持这些存储外设的热插拔。

1.1总体流程图

Ø         绿色箭头:表示插入SD卡后事件传递以及SD卡挂载

Ø         红色箭头:表示挂载成功后的消息传递流程

Ø         黄色箭头:表示MountService发出挂载/卸载SD卡的命令

1.2总体类图

n         main.cpp,vold的入口函数,系统起来会只执行vold的可执行文件,调到这个main函数中。

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

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

n         DirectVolume:位于/system/vold/DirectVolume.cpp。该类的是一个工具类,主要负责对传入的事件进行进一步的处理,block事件又可以分为:Add,Removed,Change,Noaction这四种。

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

 

总的讲,vold程序需要分层三部分,第一部分为NetlinkManager,管理接受来自kernel的UEvent消息,第二部分为VolumeManager,主要负责处理来自NetlinkManager的消息和来自java层的消息,之后真正的挂载卸载动作就需要volume负责了。

2 初始化流程

2.1 时序图

2.2 代码分析

在Android 系统启动的时候,init进程会去解析init.rc文件,在该文件中,有如下代码:

service vold /system/bin/vold

    class core

    socket vold stream 0660 root mount

    ioprio be 2

定义了一个vold的service,去执行vold程序,并创建了一个名字为vold的socket,init进程解析完后就去执行vold程序,创建与java层通信的Socket。

      在Android 源码/system/vold路径下的main.cpp,这个就是vold程序的入口,我们看看起main函数,代码如下:

int main() {      VolumeManager *vm;      CommandListener *cl;      NetlinkManager *nm;      if (!(vm = VolumeManager::Instance())) {//创建VolumeManager实例      };         if (!(nm = NetlinkManager::Instance())) {//创建NelinkManager实例      };      cl = new CommandListener(); //创建与java层socket通信的接口      vm->setBroadcaster((SocketListener *) cl);      nm->setBroadcaster((SocketListener *) cl);      if (vm->start()) { //什么都没做      }      if (process_config(vm)) {//初始化fstab          SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));      }      if (nm->start()) {//开始监听kernel上报的vold消息  }  ……      if (cl->startListener()) {//开始监听来自java层的socket消息          SLOGE("Unable to start CommandListener (%s)", strerror(errno));          exit(1);      }      while(1) {          sleep(1000);      }      exit(0);  }  

    首先,在main函数中,需要创建VolumeManager和NetlinkManager的实例,里面就做了一些初始化的动作,这里就不多说了。

 

接着,则是初始化vold与java层的socket通信接口。创建了的CommandListener实例。在上面的类图关系中,我们知道,CommandListener继承于FrameworkListener,而FrameworkListener有继承于SocketListener。先看看CommandListener的初始化代码:

  1. CommandListener::CommandListener() :  
  2.                  FrameworkListener("vold") {  
  3.     registerCmd(new DumpCmd());  
  4.     registerCmd(new VolumeCmd()); //处理volume事件  
  5.     registerCmd(new AsecCmd());  
  6.     registerCmd(new ObbCmd());  
  7.     registerCmd(new StorageCmd());  
  8.     registerCmd(new XwarpCmd());  
  9.     registerCmd(new CryptfsCmd());  
  10. }  

    在上面代码中我们看到,先以“vold”为参数构造FrameworkListener类,完成之后,则调用FrameworkListener类中的registerCmd()方法,注册一些处理方法类,而对于sd卡挂载的事件,我们先关注VolumeCmd类,它是FrameworkListener的内部类,用于处理Volume事件。接下来,看FrameworkListener的构造函数:

  1. FrameworkListener::FrameworkListener(const char *socketName) :  
  2.                             SocketListener (socketName, true) {  
  3.     mCommands = new FrameworkCommandCollection();  
  4. }  

    以之前传进来的“vold”参数构造SocketListener类,然后在FrameworkListener构造函数中,创建FrameworkCommandCollection的实例,其实它就是一个容器,用于存储之前调用的registerCmd()注册的处理方法类。下面就看SocketListener的构造函数:

  1. SocketListener::SocketListener(const char *socketName, bool listen) {  
  2.     mListen = listen;  
  3.     mSocketName = socketName; /将vold字符串存储在mSocketName变量中  
  4.     mSock = -1;  
  5.     pthread_mutex_init(&mClientsLock, NULL);  
  6.     mClients = new SocketClientCollection(); //创建socket客户端容器  
  7. }  

    其实很简单,就是做了一些变量的初始化工作,用mSocketName变量存储“vold”字符串,这个vold是很有讲究的,因为是init.rc定义的vold Service中,就创建了一个名字为vold的socket端口,后面将通过“vold”获取到该 socket端口。

 

到此,CommandListener的初始化就完成了的。

我们回到main函数中,创建了CommandListener实例之后,然后调用VolumeManger的setBroadcaster方法,将CommandListener的实例存储在mBroadcaster变量中,代码如下:

void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }

其实NetlinkManager也做了同样的设置,但我还没发现它有什么用,所以就不再关注了。

接下来就开始调用了main.cpp的process_config()方法了,在介绍之前,我必须先介绍下vold.fstab配置文件,这个配置文件就是在process_config()中被解析的,而vold.fstab配置文件,就是用于描述vold的挂载动作的,其配置例子如下:

dev_mount        sdcard         /mnt/sdcard         auto     /devices/platform/goldfish_mmc.0   

挂载命令            标签           挂载点              子分区个数               挂载路径        

我们就以上面例子来说明,意思就是将/devices/platform/goldfish_mmc.0挂载到/mnt/sdcard中,/devices/platform/goldfish_mmc.0可以认为是kernel上报上来的路径。子分区个数如果为auto则表示只有1个子分区,也可以为任何不为0的整数。如果vold.fstab解析无误,VolueManager将创建DirectVolume。

好了,下面可以看 process_config()方法了,代码如下:

 

[cpp] view plaincopy

 

  1. static int process_config(VolumeManager *vm) {  
  2.     FILE *fp;  
  3.     int n = 0;  
  4.     char line[255];  
  5.     if (!(fp = fopen("/etc/vold.fstab", "r"))) {  
  6.         return -1;  
  7.     }  
  8.     while(fgets(line, sizeof(line), fp)) {  
  9.         const char *delim = " \t";  
  10.         char *save_ptr;  
  11.         char *type, *label, *mount_point, *mount_flags, *sysfs_path;  
  12.         int flags;  
  13.         n++;  
  14.         line[strlen(line)-1] = '\0';  
  15.    
  16.         if (line[0] == '#' || line[0] == '\0')  
  17.             continue;  
  18.         if (!(type = strtok_r(line, delim, &save_ptr))) {  
  19.             goto out_syntax;  
  20.         }  
  21.         if (!(label = strtok_r(NULL, delim, &save_ptr))) {  
  22.             goto out_syntax;  
  23.         }  
  24.         if (!(mount_point = strtok_r(NULL, delim, &save_ptr))) {  
  25.             goto out_syntax;  
  26.         }  
  27.         if (!strcmp(type, "dev_mount")) {  
  28.             DirectVolume *dv = NULL;  
  29.             char *part;  
  30.             if (!(part = strtok_r(NULL, delim, &save_ptr))) {  
  31.                 goto out_syntax;  
  32.             }  
  33.             if (strcmp(part, "auto") && atoi(part) == 0) {  
  34.                 goto out_syntax;  
  35.             }  
  36.             if (!strcmp(part, "auto")) {//如果解析没有错,那么就将创建DirectVolume  
  37.                 dv = new DirectVolume(vm, label, mount_point, -1);  
  38.             } else {  
  39.                 dv = new DirectVolume(vm, label, mount_point, atoi(part));  
  40.             }  
  41.             while ((sysfs_path = strtok_r(NULL, delim, &save_ptr))) {  
  42.                 if (*sysfs_path != '/') {  
  43.                     break;  
  44.                 }  
  45.                 if (dv->addPath(sysfs_path)) {  
  46.                     goto out_fail;  
  47.                 }  
  48.             }  
  49.             if (sysfs_path)  
  50.                 flags = parse_mount_flags(sysfs_path);  
  51.             else  
  52.                 flags = 0;  
  53.             dv->setFlags(flags);  
  54.             vm->addVolume(dv); //将创建的DirectVolume添加到VolumeManager中。  
  55.         } else if (!strcmp(type, "map_mount")) {  
  56.         } else {  
  57.         }  
  58.     }  
  59.     fclose(fp);  
  60.     return 0;  
  61. }  

    该方法,通过一个wihle方法,逐行进行解析,如果认为合理,那么将拿到的信息用于创建DirectVolume实例,然后调用VolumeManager的addVolume方法,存储在mVolumes变量中。

 

好了,下面就开始看注册监听kernel的sockect端口了。就是NetLinkManager的start方法,代码如下:

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,//创建socket,返回文件描述符

                        SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {

        SLOGE("Unable to create uevent socket: %s", strerror(errno));

        return -1;

    }

    if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {

        SLOGE("Unable to set uevent socket SO_RECBUFFORCE option: %s", strerror(errno));

        return -1;

    }

    if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {

        SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));

        return -1;

    }

    if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {

        SLOGE("Unable to bind uevent socket: %s", strerror(errno));

        return -1;

    }

    mHandler = new NetlinkHandler(mSock);

    if (mHandler->start()) {

        return -1;

    }

    return 0;

}

其实就是调用socket()创建socket端口,返回描述符,经过一些设置,然后就描述符作为参数,创建的NetlinkHandler实例,然后就直接调用起start方法。看NetLinkHandler构造函数:

 

NetlinkHandler::NetlinkHandler(int listenerSocket) :

                NetlinkListener(listenerSocket) {

}

构造函数里什么都没做,NetlinkHandler继承于NetlinkListener,然后讲socket端口的描述符传进去。

NetlinkListener::NetlinkListener(int socket) :

                            SocketListener(socket, false) {

    mFormat = NETLINK_FORMAT_ASCII;

}

又是这么几句代码,NetlinkListener也是继承于SocketListener,所以还将socket描述符传进去,再次创建了SocketListener的实例,所以,在vold系统中,有两个SocketListener的实例。看其构造函数,这里的构造函数与之前的是不一样的,代码如下:

SocketListener::SocketListener(int socketFd, bool listen) {

    mListen = listen;

    mSocketName = NULL;

    mSock = socketFd;

    pthread_mutex_init(&mClientsLock, NULL);

    mClients = new SocketClientCollection();

}

其实,与上面的构造函数,还是差不多的,只是传进来的参数不一样而已,之前的是一个“vold”字符串,而这里是一个socket的描述符。

好了,构造函数创建好了,那么接着看NetlinkHandler->start()方法:

int NetlinkHandler::start() {

    return this->startListener();//指到了socketListener中了

}

这里的startListener方法是SocketListener中的,代码如下:

int SocketListener::startListener() {

    if (!mSocketName && mSock == -1) {

        return -1;

    } else if (mSocketName) {

        if ((mSock = android_get_control_socket(mSocketName)) < 0) {

            return -1;

        }

    }

    if (mListen && listen(mSock, 4) < 0) {

        return -1;

    } else if (!mListen)

        mClients->push_back(new SocketClient(mSock, false));//创建socket客户端,并添加到mClients容器中。

    if (pipe(mCtrlPipe)) {

        return -1;

    }

    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {//创建新线程

        return -1;

    }

    return 0;

}

此时的条件下,mSocketName=null,mSock!=0,继续往下看,创建了SocketClient实例,并添加到mClients容器中,用于接收客户端发过来的消息。

接着创建新的一个线程,用于读取socket客户端发过来的消息,线程执行的方法如下:

void *SocketListener::threadStart(void *obj) {

    SocketListener *me = reinterpret_cast(obj);

    me->runListener();

    pthread_exit(NULL);

    return NULL;

}

看runListener()方法:

void SocketListener::runListener() {

    SocketClientCollection *pendingList = new SocketClientCollection();

    while(1) {

        ……

        for (it = mClients->begin(); it != mClients->end(); ++it) {

            int fd = (*it)->getSocket();

            if (FD_ISSET(fd, &read_fds)) {

                pendingList->push_back(*it);

            }

        }

        pthread_mutex_unlock(&mClientsLock);

        while (!pendingList->empty()) {//客户端有消息

            it = pendingList->begin();

            SocketClient* c = *it;

            pendingList->erase(it);

            if (!onDataAvailable(c) && mListen) {//  处理消息          

            }

        }

    }

    delete pendingList;

}

在该方法中,一个while循环,不断读取socket消息,如果发现有socket消息,那么就调用方法onDataAvailable处理,该方法是在NetlinkListener方法实现的,其代码如下:

bool NetlinkListener::onDataAvailable(SocketClient *cli)

{

    int socket = cli->getSocket();

    ssize_t count;

    count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_recv(socket, mBuffer, sizeof(mBuffer)));

    if (count < 0) {

        SLOGE("recvmsg failed (%s)", strerror(errno));

        return false;

    }

    NetlinkEvent *evt = new NetlinkEvent();

    if (!evt->decode(mBuffer, count, mFormat)) {

        SLOGE("Error decoding NetlinkEvent");

    } else {

        onEvent(evt);//在NetlinkHandler被实现

    }

    return true;

就是经过了处理,跳转到了NetlinkHandler的onEvent()方法处理。好了,注册kernel监听就到此先搞一段落了。

回到了main函数中,最后,看到调用了CommandListener->startListener(),其实就是调用了SocketListener中的startListener方法。代码就不再次贴出来了,同样也是创建了一个新的线程读取socket消息,只是,发现有消息后,调用的是FrameworkListener中的onDataAvailable方法处理。

好了,到此,vold的初始化已经完成了。下面看看sd的mount流程吧。

3 SD卡mount流程

3.1时序图

3.2 流程图

3.3 代码分析

经过前面的介绍,我们知道了,在NetlinkHandler的onEvent方法中,收到了kernel的消息。其代码如下:

void NetlinkHandler::onEvent(NetlinkEvent *evt) {

    VolumeManager *vm = VolumeManager::Instance();

    const char *subsys = evt->getSubsystem();

    if (!subsys) {

        return;

    }

    if (!strcmp(subsys, "block")) {

        vm->handleBlockEvent(evt);//进一步处理

    }

}

这里就只处理block消息了,看看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处理

            hit = true;

            break;

        }

    }

}

这里的for循环遍历mVolumes,它其实是DirectVolume实例列表,在解析vold.fstab中,创建的DirectVolume实例并添加到mVolumes列表中。然后再调用DirectVolume的handleBlockEvent方法尝试处理该消息,看是否能匹配,起代码如下:

int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {

    const char *dp = evt->findParam("DEVPATH");

 

    PathCollection::iterator  it;

    for (it = mPaths->begin(); it != mPaths->end(); ++it) {//遍历vold.fstab定义的路径

        if (!strncmp(dp, *it, strlen(*it))) {//kernel上报上来的路径与vold.fstab中定义的匹配

           

            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"));

                char nodepath[255];

 

                snprintf(nodepath,

                         sizeof(nodepath), "/dev/block/vold/%d:%d",

                         major, minor);

                if (createDeviceNode(nodepath, major, minor)) {

                }

                if (!strcmp(devtype, "disk")) {//插入设备消息

                    handleDiskAdded(dp, evt);//上报一个物理分区

                } else {

                    handlePartitionAdded(dp, evt);//上报一个逻辑分区

                }

            } else if (action == NetlinkEvent::NlActionRemove) {//拔出设备消息

                if (!strcmp(devtype, "disk")) {

                    handleDiskRemoved(dp, evt);

                } else {

                    handlePartitionRemoved(dp, evt);

                }

            } else if (action == NetlinkEvent::NlActionChange) {//设备状态改变消息

                if (!strcmp(devtype, "disk")) {

                    handleDiskChanged(dp, evt);

                } else {

                    handlePartitionChanged(dp, evt);

                }

            } else {

                    SLOGW("Ignoring non add/remove/change event");

            }

            return 0;

        }

    }

    errno = ENODEV;

    return -1;

}

Kernel上报上来的消息中,有一个路径的消息,将与vold.fstab中定义的路径进行匹配,如果匹配,那么说明这个消息是有效的,那么就继续处理。

那么,kernel上报的消息也分为三类,分别是设备插入、拔出、状态改变。我们这里就先关注插入的消息吧。

那么,插入的消息,又分是物理分区还是一个逻辑分区。假如插入一个sd卡,它只有一个分区,那么上报的就是Disk消息。假如插入一个sd卡,该卡有内部又被分成多个分区,那么就先上报的是一个Dist消息,用于描述这个sd卡,后面还会上报多个消息,每个消息对应sd卡中的一个分区,也就是partition消息。

在这里,我们关注Dist消息吧,看看handleDiskAdded()方法,代码如下;

void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) {

    mDiskMajor = atoi(evt->findParam("MAJOR"));

    mDiskMinor = atoi(evt->findParam("MINOR"));

    const char *tmp = evt->findParam("NPARTS");

    if (tmp) {

        mDiskNumParts = atoi(tmp);//如果上报的是只有一个分区的sd,该变量为0

    } else {

        mDiskNumParts = 1;

    }

       mPartsEventCnt = 0;

    char msg[255];

    int partmask = 0;

    int i;

    for (i = 1; i <= mDiskNumParts; i++) {

        partmask |= (1 << i);

    }

    mPendingPartMap = partmask;

    if (mDiskNumParts == 0) {

        setState(Volume::State_Idle);//设置初始状态

    } else {

        setState(Volume::State_Pending);

    }

    snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)",

             getLabel(), getMountpoint(), mDiskMajor, mDiskMinor);//构造消息

    mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,

                                             msg, false);//发socket消息到java层

}

如果是Disk消息,那么上报的sd卡只有一个分区,所以上面的mDiskNumParts=0。看下面,调用snprintf()构造msg消息,然后调用mVm->getBroadcaster()->sendBroadcast发送到java层。其实mVm->getBroadcaster()就是放回CommandListener的实例变量,sendBroadcast就是在SocketListener中,代码如下:

void SocketListener::sendBroadcast(const char *msg) {

    pthread_mutex_lock(&mClientsLock);

    SocketClientCollection::iterator i;

    for (i = mClients->begin(); i != mClients->end(); ++i) {

        if ((*i)->sendMsg(msg)) {//发送socket消息

        }

    }

    pthread_mutex_unlock(&mClientsLock);

}

Ok,看到了吧,这里就发送了一个VolumeDiskInserted的消息到java层。但如果是系统改启动的话,kernel早早就发来了消息,但是java层还没起来呢。所以,等到mountService起来之后,就收到了socket消息了。

我们直接看mountService的onEvent()方法吧代码如下:

public boolean onEvent(int code, String raw, String[] cooked) {

   …….

 if (code == VoldResponseCode.VolumeDiskInserted) {

   new Thread() {

    public void run() {

           try {

             int rc;

             if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {

                      Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));

                            }

               } catch (Exception ex) {

                      }

                    }

                }.start();

            } else if (code == VoldResponseCode.VolumeDiskRemoved) {

             }

}

这里我们只看onEvent的处理VoldResponseCode.VolumeDiskInserted消息,我们看到,对于VolumeDiskInserted消息,mountService立刻调用了方法doMountVolume(path),其实就是通过socket对vold发送了一个条mount的命令。

所以对与java层来讲,可以发mount、unmount消息到vold中。那么现在,就看vold处理吧。

前面也介绍过,java层发送的socket消息,vold层在SocketListener中读取到,然后会在FrameworkListener的onDataAvailable()方法中处理,代码如下:

bool FrameworkListener::onDataAvailable(SocketClient *c) {

    char buffer[255];

    int len;

    len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));

    if (len < 0) {

        return false;

    } else if (!len)

        return false;

    int offset = 0;

    int i;

    for (i = 0; i < len; i++) {

        if (buffer[i] == '\0') {

            dispatchCommand(c, buffer + offset);//开始派发消息

            offset = i + 1;

        }

    }

    return true;

}

调用dispatchCommand()派发消息了,代码如下:

void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {

    ……

    for (i = mCommands->begin(); i != mCommands->end(); ++i) {

        FrameworkCommand *c = *i;

        if (!strcmp(argv[0], c->getCommand())) {

            if (c->runCommand(cli, argc, argv)) {

                SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));

            }

    }

}

一堆的处理,代码也就不贴出来了,直接看关键的部分吧。记得在CommandListener的构造函数中吗,里面调用了FrameworkListener的registerCmd()方法,注册了一些处理方法类,其实就是添加到了mCommands容器中了,这里当然需要遍历咯,找到其合适的处理方法类,然后调用其runComand()方法,看看其代码吧:

int CommandListener::VolumeCmd::runCommand(SocketClient *cli,

                                                      int argc, char **argv) {

    dumpArgs(argc, argv, -1);

    …….

    VolumeManager *vm = VolumeManager::Instance();

    int rc = 0;

    if (!strcmp(argv[1], "list")) {

        return vm->listVolumes(cli);

    } else if (!strcmp(argv[1], "debug")) {

    } else if (!strcmp(argv[1], "mount")) {//处理mount消息

        rc = vm->mountVolume(argv[2]);

    } else if (!strcmp(argv[1], "unmount")) {

        rc = vm->unmountVolume(argv[2], force, revert);

} else if (!strcmp(argv[1], "format")) {  //处理格式化消息

        rc = vm->formatVolume(argv[2]);

    } else if (!strcmp(argv[1], "share")) { //处理挂载到pc消息

        rc = vm->shareVolume(argv[2], argv[3]);

    } else if (!strcmp(argv[1], "unshare")) {

        rc = vm->unshareVolume(argv[2], argv[3]);

    } else if (!strcmp(argv[1], "shared")) {

        bool enabled = false;

        if (vm->shareEnabled(argv[2], argv[3], &enabled)) {

    }

    return 0;

}

在这里处理Volume消息,我们就只看mount消息吧,就调用了VolumeManager的mountVolume方法,代码如下:

int VolumeManager::mountVolume(const char *label) {

    Volume *v = lookupVolume(label);//找到该挂载点的Volume的实例

    if (!v) {

        return -1;

    }

    return v->mountVol();//去挂载啦

}

到Volume的mountVol()中挂载,代码如下:

int Volume::mountVol() {

  …..

}

这个方法代码量比较大,就不贴出来了,但是完成mount的动作就是在该方法中,然后呢,Volume中还包含了其他的功能方法,比如unmount、share、unshare。

好了,花了一个下午的时间整理出来,vold的初始化即sd卡的挂载流程就讲解到这吧,我这里讲流程的比较多,很多细节问题也没有讲,其实我写的文档,还是比较注册流程,消息是怎么传递的,至于细节,用到的时候,再详细看!

更多相关文章

  1. Android(安卓)Hook Java的的一个改进版本
  2. Android(安卓)DragAndDrop API 拖拽效果 交换ListView的Item值
  3. Android系统启动阶段多种快速重启系统方法试验记录
  4. 腾讯、网易云、字节跳动面试点总结—AMS在Android起到什么作用?
  5. Android实现推送消息的解决方案
  6. AsyncTask的使用和原理探究(一)
  7. Android(安卓)View相关-事件分发机制详解-View
  8. 教你如何在Android(安卓)6.0上创建系统悬浮窗
  9. Android(安卓)TV (三)(创建电视页面布局)

随机推荐

  1. 博客园app for xamarin android一款简洁
  2. Android中的sqlite简单示例
  3. android studio 3.6.0 绑定视图新特性的
  4. Android XML 解析
  5. android ActivityManagerService服务详解
  6. Eclipse Android SDK Manager下载失败解
  7. android api 完整翻译之Contacts Provide
  8. android -h 'xcopy' 不是内部或外部命令
  9. Android应用程序汉化教程
  10. Android常用DOS命令