Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> vold掛載管理

vold掛載管理

編輯:關於Android編程

看了很長時間Vold存儲模塊的相關知識,也深入的研究一段時間的Android源碼,打算把自己看過的經驗之貼、參考資料和自己的一些見解,以帖子的形式發出來,供有興趣的同仁們參考,有不對的地方請指正,我們相互交流。

1.1 vold的原理與機制分析

1.1.1 Vold 架構

\

從上圖中可知:

 

· Vold中的NetlinkManager模塊接收來自Linux內核的uevent消息。例如SD卡的插拔等動作都會引起Kernel向NM發送uevent消息。

· NetlinkManager將這些消息轉發給VolumeManager模塊。VolumeManager會對應做一些操作,然後把相關信息通過CommandListener發送給MountService,MountService根據收到的消息會發送相關的處理命令給VolumeManager做進一步的處理。例如待SD卡插入後,VolumeManager會將來自NetlinkManager的“Disk Insert”消息發送給MountService,而後MountService則發送“Mount”指令給Vold,指示它掛載這個SD卡。

· CommandListener模塊內部封裝了一個Socket用於跨進程通信。它在Vold進程中屬於監聽端(即是服務端),而它的連接端(即客戶端)則是MountService。它一方面接收來自MountService的控制命令(例如卸載存儲卡、格式化存儲卡等),另一方面VolumeManager和NetlinkManager模塊又會通過它,將一些信息發送給MountService。

1.1.2初識Vold

下面來初步的認識Vold,代碼在system\vold\main.cpp

 

int main() {
    mkdir("/dev/block/vold", 0755);  //創建一個目錄/dev/block/vold

  //創建一個VolumeManager對象,該對象為單例模式
    if (!(vm = VolumeManager::Instance())) {
        SLOGE("Unable to create VolumeManager");
        exit(1);
    };

  //創建一個NetlinkManager對象,該對象為單例模式
    if (!(nm = NetlinkManager::Instance())) {
        SLOGE("Unable to create NetlinkManager");
        exit(1);
    };

  //創建一個CommandListener對象
    cl = new CommandListener();
    vm->setBroadcaster((SocketListener *) cl);
    nm->setBroadcaster((SocketListener *) cl);

//啟動VolumeManager
    if (vm->start()) {
        SLOGE("Unable to start VolumeManager (%s)", strerror(errno));
        exit(1);
    }

//根據fstab配置文件初始化VolumeManager
    if (process_config(vm)) {
        SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));
    }

//啟動NetlinkManager對象
    if (nm->start()) {
        SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));
        exit(1);
    }
//通過往/sys/block目錄下對應的uevent文件寫“add\n”來觸發內核發送Uevent消息
    coldboot("/sys/block");

//啟動CommandListener
    if (cl->startListener()) {
        SLOGE("Unable to start CommandListener (%s)", strerror(errno));
        exit(1);
    }
}
 

1.2NetlinkManager模塊的分析

1.2.1NetlinkManager架構流程圖

 

\

 

 

上圖中的虛線為啟動是的調用流程。 (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來讀取數據)  

1.2.2start的分析

NetlinkManager模塊將使用Netlink套接字實現用戶進程與內核進程通信的一種特殊的進程間通信(IPC) ,一起看下面代碼
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;  
    // 創建一個socket用於內核空間和用戶空間的異步通信,監控系統的hotplug事件  
    if ((mSock = socket(PF_NETLINK,  
                        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;  
    }  
    // 利用新創建的socket實例化一個NetlinkHandler類對象,NetlinkHandler繼承了類NetlinkListener,      
    // NetlinkListener又繼承了類SocketListener      
    mHandler = new NetlinkHandler(mSock);  
    if (mHandler->start()) {  //啟動NetlinkHandler  
        SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));  
        return -1;  
    }  
    return 0;  
} 
NetlinkHandler構造  
NetlinkHandler::NetlinkHandler(int listenerSocket) :
                NetlinkListener(listenerSocket) {
}
繼承父類
NetlinkListener::NetlinkListener(int socket) :
                            SocketListener(socket, false) {
    mFormat = NETLINK_FORMAT_ASCII;
}
這裡又是構造了一個SockListener的實例,傳入了上面創建的socket標識。

 

接著調用的start()函數,也是最終實現在SockListener的startListener()。

int NetlinkHandler::start() {  
    return this->startListener();  
}  
  
int SocketListener::startListener() {  
  
    if (!mSocketName && mSock == -1) {  
        SLOGE("Failed to start unbound listener");  
        errno = EINVAL;  
        return -1;  
    } else if (mSocketName) {  
        if ((mSock = android_get_control_socket(mSocketName)) < 0) {  
            SLOGE("Obtaining file descriptor socket '%s' failed: %s",  
                 mSocketName, strerror(errno));  
            return -1;  
        }  
    }  
  
    if (mListen && listen(mSock, 4) < 0) {  
        SLOGE("Unable to listen on socket (%s)", strerror(errno));  
        return -1;  
    } else if (!mListen)  
        mClients->push_back(new SocketClient(mSock, false));  
  
    if (pipe(mCtrlPipe)) {  
        SLOGE("pipe failed (%s)", strerror(errno));  
        return -1;  
    }  
  
    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {  
        SLOGE("pthread_create (%s)", strerror(errno));  
        return -1;  
    }  
  
    return 0;  
}  
  
void *SocketListener::threadStart(void *obj) {  
    SocketListener *me = reinterpret_cast(obj);  
  
    me->runListener();  
    pthread_exit(NULL);  
    return NULL;  
}  
  
void SocketListener::runListener() {  
  
    SocketClientCollection *pendingList = new SocketClientCollection();  
  
    while(1) { // 死循環,一直監聽  
        SocketClientCollection::iterator it;  
        fd_set read_fds;  
        int rc = 0;  
        int max = -1;  
  
        FD_ZERO(&read_fds); //清空文件描述符集read_fds   
  
        if (mListen) {  
            max = mSock;  
            FD_SET(mSock, &read_fds); //添加文件描述符到文件描述符集read_fds  
        }  
  
        FD_SET(mCtrlPipe[0], &read_fds); //添加管道的讀取端文件描述符到read_fds  
        if (mCtrlPipe[0] > max)  
            max = mCtrlPipe[0];  
  
        pthread_mutex_lock(&mClientsLock); //對容器mClients的操作需要加鎖  
        for (it = mClients->begin(); it != mClients->end(); ++it) {  
            int fd = (*it)->getSocket();  
            FD_SET(fd, &read_fds); ////遍歷容器mClients的所有成員,調用內聯函數getSocket()獲取文件描述符,並添加到文件描述符集read_fds  
            if (fd > max)  
                max = fd;  
        }  
        pthread_mutex_unlock(&mClientsLock);  
        // 等待文件描述符中某一文件描述符或者說socket有數據到來  
        if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {  
            if (errno == EINTR)  
                continue;  
            SLOGE("select failed (%s)", strerror(errno));  
            sleep(1);  
            continue;  
        } else if (!rc)  
            continue;  
  
        if (FD_ISSET(mCtrlPipe[0], &read_fds))  
            break;  
        if (mListen && FD_ISSET(mSock, &read_fds)) { //監聽套接字處理  
            struct sockaddr addr;  
            socklen_t alen;  
            int c;  
  
            do {  
                alen = sizeof(addr);  
                c = accept(mSock, &addr, &alen); //接收鏈接請求,建立連接,如果成功c即為建立鏈接後的數據交換套接字,將其添加到mClient容器  
            } while (c < 0 && errno == EINTR);  
            if (c < 0) {  
                SLOGE("accept failed (%s)", strerror(errno));  
                sleep(1);  
                continue;  
            }  
            pthread_mutex_lock(&mClientsLock);  
            mClients->push_back(new SocketClient(c, true));  
            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) {  
            int fd = (*it)->getSocket();  
            if (FD_ISSET(fd, &read_fds)) {  
                pendingList->push_back(*it);  
            }  
        }  
        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 and our sockets are 
             * connection-based, remove and destroy it */  
            // ****** onDataAvailable在NetlinkListener中實現*********  
             if (!onDataAvailable(c) && mListen) {  
                /* Remove the client from our array */  
                pthread_mutex_lock(&mClientsLock);  
                for (it = mClients->begin(); it != mClients->end(); ++it) {  
                    if (*it == c) {  
                        mClients->erase(it);  
                        break;  
                    }  
                }  
                pthread_mutex_unlock(&mClientsLock);  
                /* Remove our reference to the client */  
                c->decRef();  
            }  
        }  
    }  
    delete pendingList;  
}
SocketListener::runListener是線程真正執行的函數:mListen成員用來判定是否監聽套接字,Netlink套接字屬於udp套接字,非監聽套接字,該函數的主要功能體現在,如果該套接字有數據到來,就調用函數onDataAvailable讀取數據
bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
    int socket = cli->getSocket();
    ssize_t count;
    uid_t uid = -1;

    count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv(
                                       socket, mBuffer, sizeof(mBuffer), &uid)); //從socket中讀取event信息
    if (count < 0) {
        if (uid > 0)
            LOG_EVENT_INT(65537, uid);
        SLOGE("recvmsg failed (%s)", strerror(errno));
        return false;
    }

    NetlinkEvent *evt = new NetlinkEvent();
    if (!evt->decode(mBuffer, count, mFormat)) {   //調用NetlinkEvent解析event
        SLOGE("Error decoding NetlinkEvent");
    } else {
        onEvent(evt);  //傳遞event給子類NetlinkHandler處理
    }

    delete evt;
    return true;
}
  到NetlinkHandler.cpp中:
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")) {   //EVENT為block類型
        int action = evt->getAction();

        vm->updatePullOutState(evt);
        vm->setHotPlug(true);
        vm->handleBlockEvent(evt);  //把event交給VolumeManager處理
        vm->setHotPlug(false);

    }

1.2.3 NM模塊的總結

NM模塊的功能就是從Kernel接收Uevent消息,然後轉換成一個NetlinkEvent對象,最後會調用VM的處理函數來處理這個NetlinkEvent對象。  

1.3VolumeManager模塊的分析

1.3.1 Vold使用VM模塊的流程

\

調用Instance創建一個VM對象。

調用setBroadcaster設置CL對象

調用start啟動VM。

調用process_config配置VM。

1.3.2 VM的創建

 

VolumeManager *VolumeManager::Instance() {
    if (!sInstance)
        sInstance = new VolumeManager();
    return sInstance;
}

 

 

VolumeManager::VolumeManager() {
    mDebug = false;
    mVolumes = new VolumeCollection();  //一個容器,保存Volume的集合
    mActiveContainers = new AsecIdCollection();
    mBroadcaster = NULL;   //指向socketlisten,用於發送掛載事件
    mUmsSharingCount = 0;
    mSavedDirtyRatio = -1;
    mUmsDirtyRatio = dirtyRatio();
    mVolManagerDisabled = 0;
    mIsHotPlug= false;
    mUseBackupContainers =false;
    mIsFirstBoot = false;
    mIpoState = State_Ipo_Start;
}
 

 

1.3.3 VM的啟動

int VolumeManager::start() {
    return 0;   //VM的啟動什麼也沒有做
}

1.3.4 process_config分析

這個函數用於解析指定的配置文件,根據內容構造DirectVolume以及父類然後保存進VolumeManager的容器中,供VolumeManager用於掛載事件的管理
static int process_config(VolumeManager *vm)
{
    char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
    fstab = fs_mgr_read_fstab(fstab_filename);   //讀取fstab文件
    if (!fstab) {
        SLOGE("failed to open %s\n", fstab_filename);
        return -1;
    }
    /* Loop through entries looking for ones that vold manages */
    for (i = 0; i < fstab->num_entries; i++) {
        if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
            DirectVolume *dv = NULL;
            flags = 0;
 
            /* Set any flags that might be set for this volume */
            if (fs_mgr_is_nonremovable(&fstab->recs[i])) {
                flags |= VOL_NONREMOVABLE;
            }
            if (fs_mgr_is_encryptable(&fstab->recs[i])) {
                flags |= VOL_ENCRYPTABLE;
            }
            /* Only set this flag if there is not an emulated sd card */
            if (fs_mgr_is_noemulatedsd(&fstab->recs[i]) &&
                !strcmp(fstab->recs[i].fs_type, "vfat")) {
                flags |= VOL_PROVIDES_ASEC;
            }
            dv = new DirectVolume(vm, &(fstab->recs[i]), flags);
            if (dv->addPath(fstab->recs[i].blk_device)) {
                SLOGE("Failed to add devpath %s to volume %s",
                      fstab->recs[i].blk_device, fstab->recs[i].label);
                goto out_fail;
            }
            vm->addVolume(dv);  //添加到volumemanager容器
        }
    }
}
fstab配置文件
# Android fstab file.
#                                         
# The filesystem that contains the filesystem checker binary (typically /system) cannot
# specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK

/devices/platform/mtk-msdc.0/mmc_host   auto      vfat      defaults        voldmanaged=sdcard0:emmc@fat,noemulatedsd
/devices/platform/mtk-msdc.1/mmc_host   auto      vfat      defaults        voldmanaged=sdcard1:auto

 

1.3.5 DirectVolume分析

  DirectVolume從Volume類派生,可把它看成是一個外部存儲卡(例如一張SD卡)在代碼中的代表物。它封裝了對外部存儲卡的操作,例如加載/卸載存儲卡、格式化存儲卡等 DirectVolume構造
DirectVolume::DirectVolume(VolumeManager *vm,const char *label,
                           const char*mount_point, int partIdx) :
              Volume(vm, label, mount_point) {//初始化基類
   /*
      注意其中的參數:
     label為”sdcard”,mount_point為”/mnt/sdcard”,partIdx為1    
*/
   mPartIdx = partIdx;
//PathCollection定義為typedef android::List PathCollection
//其實就是一個字符串list
    mPaths= new PathCollection();
    for(int i = 0; i < MAX_PARTITIONS; i++)
       mPartMinors[i] = -1;
   mPendingPartMap = 0;
   mDiskMajor = -1;  //存儲設備的主設備號
   mDiskMinor = -1;  //存儲設備的次設備號,一個存儲設備將由主次兩個設備號標識。
   mDiskNumParts = 0;
   //設置狀態為NoMedia
   setState(Volume::State_NoMedia);
}
//再來看addPath函數,它主要目的是添加設備在sysfs中的路徑,
int DirectVolume::addPath(const char *path) {
   mPaths->push_back(strdup(path));
    return0;
}
addPath把和某個存儲卡接口相關的設備路徑與這個DirectVolume綁定到一起,並且這個設備路徑和Uevent中的DEVPATH是對應的,這樣就可以根據Uevent的DEVPATH找到是哪個存儲卡的DirectVolume發生了變動。當然手機上目前只有一個存儲卡接口,所以Vold也只有一個DirectVolume。

1.3.6 VM與NM交互

在分析NM模塊的數據處理時發現,NM模塊接收到Uevent事件後,會調用VM模塊進行處理,如果Uevent是block子系統,則調用handleBlockEvent
void VolumeManager::handleBlockEvent(NetlinkEvent*evt) {
    constchar *devpath = evt->findParam("DEVPATH");
 
/*
前面在process_config中構造的DirectVolume對象保存在了mVolumes中,它的定義如下:
typedef android::ListVolumeCollection,也是一個列表。
  注意它保存的是Volume指針,而我們的DirectVolume是從Volume派生的
*/
   VolumeCollection::iterator it;
    boolhit = false;
for (it = mVolumes->begin(); it !=mVolumes->end(); ++it) {
        //調用每個Volume的handleBlockEvent事件,實際上將調用
        //DirectVolume的handleBlockEvent函數。
        if(!(*it)->handleBlockEvent(evt)) {
           hit = true;
           break;
        }
    }
}
int DirectVolume::handleBlockEvent(NetlinkEvent*evt) {
    constchar *dp = evt->findParam("DEVPATH");
 
PathCollection::iterator  it;
//將Uevent的DEVPATH和addPath添加的路徑進行對比,判斷屬不屬於自己管理的范圍。
    for(it = mPaths->begin(); it != mPaths->end(); ++it) {
        if(!strncmp(dp, *it, strlen(*it))) {
           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 {
                    /*
對於有分區的SD卡,先收到上面的“disk”消息,然後每個分區就會收到
                   一個分區添加消息。
                   */
                    handlePartitionAdded(dp,evt);
               }
           } else if (action == NetlinkEvent::NlActionRemove) {
                ......
           } else if (action == NetlinkEvent::NlActionChange) {
              ......
           }
           ......
           return 0;
        }
    }
    errno= ENODEV;
    return-1;
}
 

1.4 commandlisten模塊分析

1.4.1commandlisten構造

/system/vold/main.cpp
cl = new CommandListener();  
vm->setBroadcaster((SocketListener *) cl);  
nm->setBroadcaster((SocketListener *) cl);  
  
...  
  
/* 
* Now that we're up, we can respond to commands 
*/  
if (cl->startListener()) {  
   SLOGE("Unable to start CommandListener (%s)", strerror(errno));  
   exit(1);  
}
構造函數/system/vold/CommandListener.cpp
CommandListener::CommandListener() :
                 FrameworkListener("vold", true) {
    registerCmd(new DumpCmd());
    registerCmd(new VolumeCmd());
    registerCmd(new AsecCmd());
    registerCmd(new ObbCmd());
    registerCmd(new StorageCmd());
    registerCmd(new XwarpCmd());
    registerCmd(new CryptfsCmd());
    registerCmd(new FstrimCmd());
    //M{
#ifndef MTK_EMULATOR_SUPPORT
    registerCmd(new USBCmd());
#endif
    registerCmd(new CDROMCmd());
    //}M
#if defined (ENG_BUILD_ENG)    
    registerCmd(new SilkRoad());
#endif
}

構造一個父類FrameworkListener
FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :  
                            SocketListener(socketName, true, withSeq) {  
    init(socketName, withSeq);  
}
void FrameworkListener::init(const char *socketName, bool withSeq) {  
    mCommands = new FrameworkCommandCollection();  
    errorRate = 0;  
    mCommandCount = 0;  
    mWithSeq = withSeq;  
}
SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) {
    init(socketName, -1, listen, useCmdNum);
}
void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {
    mListen = listen;
    mSocketName = socketName;
    mSock = socketFd;
    mUseCmdNum = useCmdNum;
    pthread_mutex_init(&mClientsLock, NULL);
    mClients = new SocketClientCollection();
}

1.4.2 Command注冊

CommandListener的構造函數中調用父類的注冊函數register, 注冊commad到FrameworkListener的mCommands容器中
void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
    mCommands->push_back(cmd);
}
CommandListener::VolumeCmd::VolumeCmd() :
                 VoldCommand("volume") {
}
VoldCommand::VoldCommand(const char *cmd) :
              FrameworkCommand(cmd)  {
}
FrameworkCommand::FrameworkCommand(const char *cmd) {
    mCommand = cmd;
}

將volume這個command注冊到mCommands這個容器中之後,目的是當FrameworkListerer從SlocketListener接收到command的時候,會依據mCommands 中的command進行解析篩選判斷分發,調用對應的command執行類

1.4.3startListener的分析和數據處理

int SocketListener::startListener() {  
  
    if (!mSocketName && mSock == -1) {  
        SLOGE("Failed to start unbound listener");  
        errno = EINVAL;  
        return -1;  
    } else if (mSocketName) {  
        if ((mSock = android_get_control_socket(mSocketName)) < 0) {//獲取socket的文件描述符,這裡是獲取Vold這個socket的  
            SLOGE("Obtaining file descriptor socket '%s' failed: %s",  
                 mSocketName, strerror(errno));  
            return -1;  
        }  
        SLOGV("got mSock = %d for %s", mSock, mSocketName);  
    }  
  
    if (mListen && listen(mSock, 4) < 0) {  
        SLOGE("Unable to listen on socket (%s)", strerror(errno));  
        return -1;  
    } else if (!mListen)//是否正常監聽socket  
        mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));  
  
    if (pipe(mCtrlPipe)) {//新建管道,保存文件描述符到數組  
        SLOGE("pipe failed (%s)", strerror(errno));  
        return -1;  
    }  
  
    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {//開了一個線程來處理  
        SLOGE("pthread_create (%s)", strerror(errno));  
        return -1;  
    }  
  
    return 0;  
} 
開啟線程用於監聽
void *SocketListener::threadStart(void *obj) {  
    SocketListener *me = reinterpret_cast(obj);//threadStart為static函數,上面開線程創建的時候傳了this,這裡需要轉換一個一樣bit位的SocketListener指針  
  
    me->runListener();//SocketListener真正的執行函數  
    pthread_exit(NULL);  
    return NULL;  
} 
對socket監聽處理
void SocketListener::runListener() {  
  
    SocketClientCollection *pendingList = new SocketClientCollection();//暫存mClients中的SocketClient  
  
    while(1) {  
        SocketClientCollection::iterator it;  
        fd_set read_fds;  
        int rc = 0;  
        int max = -1;  
  
        FD_ZERO(&read_fds);//清空文件描述符集read_fds   
  
        if (mListen) {  
            max = mSock;  
            FD_SET(mSock, &read_fds);//如果正常的監聽,這裡就把之前獲得的vold的文件描述符添加進去  
        }  
  
        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) {//遍歷mClients,獲取fd 添加到read_fds  
            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);  
  
//linux下socket編程的select,這裡檢測read_fds集合裡面是否有可讀的,也就是有沒有數據過來,沒有數據的文件描述符會從read_fds中被剔除,這裡的select設置time out為NULL,阻塞操作,直到read_fds集合中描述符有變化  
        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))//如果匿名管道有數據可讀,就退出  
            break;  
        if (mListen && FD_ISSET(mSock, &read_fds)) {//如果是正常監聽的 mListen 為true,然後mSock這個描述符有可讀數據,就創建鏈接,新建異步處理的SocketClient加入到mClients容器,這裡的mSock 是vold這個套接字的描述符  
            struct sockaddr addr;  
            socklen_t alen;  
            int c;  
  
            do {  
                alen = sizeof(addr);  
                c = accept(mSock, &addr, &alen);  
                SLOGV("%s got %d from accept", mSocketName, c);  
            } while (c < 0 && errno == EINTR);  
            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) {//把上面有請求建立鏈接的Client加入到pendingList 容器中,後面處理  
            int fd = (*it)->getSocket();  
            if (FD_ISSET(fd, &read_fds)) {  
                pendingList->push_back(*it);  
            }  
        }  
        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 and our sockets are 
             * connection-based, remove and destroy it */  
            if (!onDataAvailable(c) && mListen) {//調用到FrameworkListener 中onDataAvailable處理Socket事件  
                /* Remove the client from our array */  
                SLOGV("going to zap %d for %s", c->getSocket(), mSocketName);  
                pthread_mutex_lock(&mClientsLock);  
                for (it = mClients->begin(); it != mClients->end(); ++it) {  
                    if (*it == c) {  
                        mClients->erase(it);//處理完成之後,從容器中移除這次的監聽到的SocketClient  
                        break;  
                    }  
                }  
                pthread_mutex_unlock(&mClientsLock);  
                /* Remove our reference to the client */  
                c->decRef();  
            }  
        }  
    }  
    delete pendingList;  
}  
SocketListener的核心處理函數是onDataAvailable
bool FrameworkListener::onDataAvailable(SocketClient *c) {  
    char buffer[255];  
    int len;  
  
    len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));//讀Socket 內容保存到buffer  
    if (len < 0) {  
        SLOGE("read() failed (%s)", strerror(errno));  
        return false;  
    } else if (!len)  
        return false;  
  
    int offset = 0;  
    int i;  
  
    for (i = 0; i < len; i++) {  
        if (buffer[i] == '\0') {//一次傳入一個字符串  
            //分發命令,最終會調用對應命令對象的runCommand進行函數處理。
            dispatchCommand(c, buffer + offset);  
            offset = i + 1;  
        }  
    }  
    return true;  
}  
調用dispatchcommand
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {  
    FrameworkCommandCollection::iterator i;  
    int argc = 0;  
    char *argv[FrameworkListener::CMD_ARGS_MAX];  
    char tmp[255];  
  
...//解析判斷command buffer  
  
    for (i = mCommands->begin(); i != mCommands->end(); ++i) {//遍歷之前register到FrameworkListener中的FrameworkCommand容器  
        FrameworkCommand *c = *i;  
  
        if (!strcmp(argv[0], c->getCommand())) {//其中有注冊“Volume”,如果這裡是Volume開頭的command,那麼就調用,Volume構造的時候所構造的父類FrameworkCommand中的runCommand函數  
            if (c->runCommand(cli, argc, argv)) {  
                SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));  
            }  
            goto out;  
        }  
    }   
} 
以Volume這個runcommand為例 /system/vold/CommandListener.cpp中
int CommandListener::VolumeCmd::runCommand(SocketClient *cli,  
                                                      int argc, char **argv) {  
    dumpArgs(argc, argv, -1);  
  
    if (argc < 2) {  
        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);  
        return 0;  
    }  
  
    VolumeManager *vm = VolumeManager::Instance();//獲取已經存在的VolumeManager實例  
  
...  
  
else if (!strcmp(argv[1], "mount")) {//判斷command 內容  
        if (argc != 3) {  
            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount ", false);  
            return 0;  
        }  
        rc = vm->mountVolume(argv[2]);//交給VolumeManager來對Volume進行操作  
    }  
  
...  
  
} 

1.4.4 commandlisten 數據處理流程圖

\    

1.5 mountservice模塊的介紹

1.5.1mountservice構造

public MountService(Context context) {  
        mContext = context;  
  
        synchronized (mVolumesLock) {  
            readStorageListLocked(); // 解析/frameworks/base/core/res/res/xml/storage_list.xml保存volume到 MountService的list :mVolumes中  
        }  
  
        // XXX: This will go away soon in favor of IMountServiceObserver  
        mPms = (PackageManagerService) ServiceManager.getService("package");  
  
        mHandlerThread = new HandlerThread("MountService");  
        mHandlerThread.start();  
        mHandler = new MountServiceHandler(mHandlerThread.getLooper());//新建消息處理handler  
  
        // Watch for user changes  
        final IntentFilter userFilter = new IntentFilter();  
        userFilter.addAction(Intent.ACTION_USER_ADDED);  
        userFilter.addAction(Intent.ACTION_USER_REMOVED);  
        mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);//注冊廣播接收  
  
        // Watch for USB changes on primary volume  
        final StorageVolume primary = getPrimaryPhysicalVolume();  
        if (primary != null && primary.allowMassStorage()) {  
            mContext.registerReceiver(  
                    mUsbReceiver, new IntentFilter(UsbManager.ACTION_USB_STATE), null, mHandler);  
        }  
  
        // Add OBB Action Handler to MountService thread.  
        mObbActionHandler = new ObbActionHandler(mHandlerThread.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);//創建 vold 的監聽接收,用於接收system中Vold的socket消息  
  
        Thread thread = new Thread(mConnector, VOLD_TAG);  
        thread.start();//啟動線程,NativeDaemonConnector實現了Runnable接口,實現在 run中  
  
        // Add ourself to the Watchdog monitors if enabled.  
        if (WATCHDOG_ENABLE) {  
            Watchdog.getInstance().addMonitor(this);  
        }  
    } 
 

1.5.2構造了NativeDaemonConnector用來接收來自下層的socket消息

NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,  
        int responseQueueSize, String logTag, int maxLogSize) {  
    mCallbacks = callbacks; //回調  
    mSocket = socket; // socket名稱  
    mResponseQueue = new ResponseQueue(responseQueueSize);//構建一個響應隊列  
    mSequenceNumber = new AtomicInteger(0);  
    TAG = logTag != null ? logTag : "NativeDaemonConnector";  
  }
開啟監測線程的run方法
public void run() {  
    HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler"); //TAG 為 VoldConnector,這裡新建一個名為VoldConnector.CallbackHandler的消息處理線程,用於下面接收到vold 的socket之後的處理  
    thread.start();  
    mCallbackHandler = new Handler(thread.getLooper(), this); //創建handler 用於分發消息  
     while (true) {  
        try {  
            listenToSocket();// while 循環 監聽socket  
        } catch (Exception e) {  
            loge("Error in NativeDaemonConnector: " + e);  
            SystemClock.sleep(5000);  
        }  
    }  
} 
private void listenToSocket() throws IOException {  
    LocalSocket socket = null;  
  
    try {  
        socket = new LocalSocket();  //創建本地socket  
        LocalSocketAddress address = new LocalSocketAddress(mSocket,  
                LocalSocketAddress.Namespace.RESERVED);//獲得服務端vold socket的地址  
  
        socket.connect(address);//連接  
  
        InputStream inputStream = socket.getInputStream();  
        synchronized (mDaemonLock) {  
            mOutputStream = socket.getOutputStream();  
        }//獲取輸入輸出流  
  
        mCallbacks.onDaemonConnected();//回調,在MountService中執行,初始化一些Volume狀態信息  
  
        byte[] buffer = new byte[BUFFER_SIZE];  
        int start = 0;  
  
        while (true) {  
            int count = inputStream.read(buffer, start, BUFFER_SIZE - start);//讀取數據到buffer  
            if (count < 0) {//連接斷開,跳出當前while ,外部while循環 重新調用該函數連接  
                loge("got " + count + " reading with start = " + start);  
                break;  
            }  
  
  
            // Add our starting point to the count and reset the start.  
            count += start;  
            start = 0;  
  
            for (int i = 0; i < count; i++) {  
                if (buffer[i] == 0) {  
                    final String rawEvent = new String(  
                            buffer, start, i - start, Charsets.UTF_8);  
                    log("RCV <- {" + rawEvent + "}");  
  
                    try {  
                        final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(  
                                rawEvent);   //解析成event 保存  
                        if (event.isClassUnsolicited()) {    //判斷event的code范圍 code >= 600 && code < 700  
                            // TODO: migrate to sending NativeDaemonEvent instances  
                            mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage( //發送消息,把event交給handle來分發處理  
                                    event.getCode(), event.getRawEvent()));  
                        } else {  
                            mResponseQueue.add(event.getCmdNumber(), event);//加入到響應隊列  
                        }  
                    } catch (IllegalArgumentException e) {  
                        log("Problem parsing message: " + rawEvent + " - " + e);  
                    }  
  
                    start = i + 1;  
                }  
            }
在NativeDaemonConnector中的handle處理為:
public boolean handleMessage(Message msg) {  
    String event = (String) msg.obj;  
    try {  
        if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) { //回調到MountService 的onEent函數  
            log(String.format("Unhandled event '%s'", event));  
        }  
    } catch (Exception e) {  
        loge("Error handling '" + event + "': " + e);  
    }  
    return true;  
} 
MountService中onEvent
public boolean onEvent(int code, String raw, String[] cooked) {  
        if (DEBUG_EVENTS) {  
            StringBuilder builder = new StringBuilder();  
            builder.append("onEvent::");  
            builder.append(" raw= " + raw);  
            if (cooked != null) {  
                builder.append(" cooked = " );  
                for (String str : cooked) {  
                    builder.append(" " + str);  
                }  
            }  
            Slog.i(TAG, builder.toString());  
        }  
        if (code == VoldResponseCode.VolumeStateChange) { //根據 Vold的Code 執行  
            /* 
             * One of the volumes we're managing has changed state. 
             * Format: "NNN Volume 

1.5.3MountService下發command

MountService中對Volume的各種操作都是需要轉換成符合Vold中socket command,這樣Vold中才能正確的解析識別調用
private int doMountVolume(String path) {  
    int rc = StorageResultCode.OperationSucceeded;  
  
    final StorageVolume volume;  
    synchronized (mVolumesLock) {  
        volume = mVolumesByPath.get(path);  
    }  
  
    if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);  
    try {  
        mConnector.execute("volume", "mount", path);// 調用到NativeDaemonConnector中的execute  
    } 
NativeDaemonConnector的execute
public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args)  
            throws NativeDaemonConnectorException {  
        final ArrayList events = Lists.newArrayList();  
  
        final int sequenceNumber = mSequenceNumber.incrementAndGet();  
        final StringBuilder cmdBuilder =  
                new StringBuilder(Integer.toString(sequenceNumber)).append(' ');  
        final long startTime = SystemClock.elapsedRealtime();  
  
        makeCommand(cmdBuilder, cmd, args); //轉換制作成標准的Command  
  
        final String logCmd = cmdBuilder.toString(); /* includes cmdNum, cmd, args */  
        log("SND -> {" + logCmd + "}");  
  
        cmdBuilder.append('\0');  
        final String sentCmd = cmdBuilder.toString(); /* logCmd + \0 */  
  
        synchronized (mDaemonLock) {  
            if (mOutputStream == null) {  
                throw new NativeDaemonConnectorException("missing output stream");  
            } else {  
                try {  
                    mOutputStream.write(sentCmd.getBytes(Charsets.UTF_8)); //通過在listenToSocket中獲取的輸出流,寫入轉換好的sentCmd  
                } catch (IOException e) {  
                    throw new NativeDaemonConnectorException("problem sending command", e);  
                }  
            }  
        }  
}

1.5.4 Mountservice 數據處理流程圖

\    

 

         
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved