| 1 總體介紹 
   在Android 中,當SD卡插入系統之後,系統會自動掛載。Vold 就是負責掛載SD卡的,vold 的全稱是volume daemon。實際上是負責完成系統的CDROM,USB 大容量存儲,MMC 卡等擴展存儲的掛載任務自動完成的守護進程。它提供的主要特點是支持這些存儲外設的熱插拔。 1.1總體流程圖
 
  
 O         綠色箭頭:表示插入SD卡後事件傳遞以及SD卡掛載 O         紅色箭頭:表示掛載成功後的消息傳遞流程 O         黃色箭頭:表示MountService發出掛載/卸載SD卡的命令 1.2總體類圖
 
 
         main.cpp,vold的入口函數,系統起來會只執行vold的可執行文件,調到這個main函數中。         NetlinkManager.cpp位於源碼位置/system/vold/NetlinkManager.cpp。該類的主要通過引用NetlinkHandler類中的onEvent()方法來接收來自內核的事件消息,NetlinkHandler位於/system/vold/NetlinkHandler.cpp。         VolumeManager:位於源碼位置/system/vold/VolumeManager.cpp。該類的主要作用是接收經過NetlinkManager處理過後的事件消息。         DirectVolume:位於/system/vold/DirectVolume.cpp。該類的是一個工具類,主要負責對傳入的事件進行進一步的處理,block事件又可以分為:Add,Removed,Change,Noaction這四種。         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函數,代碼如下: 首先,在main函數中,需要創建VolumeManager和NetlinkManager的實例,裡面就做了一些初始化的動作,這裡就不多說了。複製代碼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);
}
 接著,則是初始化vold與java層的socket通信接口。創建了的CommandListener實例。在上面的類圖關係中,我們知道,CommandListener繼承於FrameworkListener,而FrameworkListener有繼承於SocketListener。先看看CommandListener的初始化代碼: 在上面代碼中我們看到,先以「vold」為參數構造FrameworkListener類,完成之後,則調用FrameworkListener類中的registerCmd()方法,註冊一些處理方法類,而對於sd卡掛載的事件,我們先關注VolumeCmd類,它是FrameworkListener的內部類,用於處理Volume事件。接下來,看FrameworkListener的構造函數:複製代碼CommandListener::CommandListener() :
                 FrameworkListener("vold") {
    registerCmd(new DumpCmd());
    registerCmd(new VolumeCmd()); //處理volume事件
    registerCmd(new AsecCmd());
    registerCmd(new ObbCmd());
    registerCmd(new StorageCmd());
    registerCmd(new XwarpCmd());
    registerCmd(new CryptfsCmd());
}
複製代碼FrameworkListener::FrameworkListener(const char *socketName) :SocketListener (socketName, true) {
    mCommands = new FrameworkCommandCollection();
}
以之前傳進來的「vold」參數構造SocketListener類,然後在FrameworkListener構造函數中,創建FrameworkCommandCollection的實例,其實它就是一個容器,用於存儲之前調用的registerCmd()註冊的處理方法類。下面就看SocketListener的構造函數:
 其實很簡單,就是做了一些變量的初始化工作,用mSocketName變量存儲「vold」字符串,這個vold是很有講究的,因為是init.rc定義的vold Service中,就創建了一個名字為vold的socket端口,後面將通過「vold」獲取到該 socket端口。複製代碼SocketListener::SocketListener(const char *socketName, bool listen) {
    mListen = listen;
    mSocketName = socketName; /將vold字符串存儲在mSocketName變量中
    mSock = -1;
    pthread_mutex_init(&mClientsLock, NULL);
    mClients = new SocketClientCollection(); //創建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()方法了,代碼如下: 複製代碼static int process_config(VolumeManager *vm) {
    FILE *fp;
    int n = 0;
    char line[255];
    if (!(fp = fopen("/etc/vold.fstab", "r"))) {
        return -1;
    }
    while(fgets(line, sizeof(line), fp)) {
        const char *delim = " \t";
        char *save_ptr;
        char *type, *label, *mount_point, *mount_flags, *sysfs_path;
        int flags;
        n++;
        line[strlen(line)-1] = '\0';
 
        if (line[0] == '#' || line[0] == '\0')
            continue;
        if (!(type = strtok_r(line, delim, &save_ptr))) {
            goto out_syntax;
        }
        if (!(label = strtok_r(NULL, delim, &save_ptr))) {
            goto out_syntax;
        }
        if (!(mount_point = strtok_r(NULL, delim, &save_ptr))) {
            goto out_syntax;
        }
        if (!strcmp(type, "dev_mount")) {
            DirectVolume *dv = NULL;
            char *part;
            if (!(part = strtok_r(NULL, delim, &save_ptr))) {
                goto out_syntax;
            }
            if (strcmp(part, "auto") && atoi(part) == 0) {
                goto out_syntax;
            }
            if (!strcmp(part, "auto")) {//如果解析沒有錯,那麼就將創建DirectVolume
                dv = new DirectVolume(vm, label, mount_point, -1);
            } else {
                dv = new DirectVolume(vm, label, mount_point, atoi(part));
            }
            while ((sysfs_path = strtok_r(NULL, delim, &save_ptr))) {
                if (*sysfs_path != '/') {
                    break;
                }
                if (dv->addPath(sysfs_path)) {
                    goto out_fail;
                }
            }
            if (sysfs_path)
                flags = parse_mount_flags(sysfs_path);
            else
                flags = 0;
            dv->setFlags(flags);
            vm->addVolume(dv); //將創建的DirectVolume添加到VolumeManager中。
        } else if (!strcmp(type, "map_mount")) {
        } else {
        }
    }
    fclose(fp);
    return 0;
}
該方法,通過一個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<SocketListener *>(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.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 == '\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卡的掛載流程就講解到這吧,我這裡講流程的比較多,很多細節問題也沒有講,其實我寫的文檔,還是比較註冊流程,消息是怎麼傳遞的,至於細節,用到的時候,再詳細看! 
 
 
 |