在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Android/ 第9章 ?深入理解Vold和Rild
第4章 ?深入理解 Zygote
第10章 深入理解MediaScanner
第3章 ?深入理解init
第8章 ?深入理解Surface系統(tǒng)
第5章 深入理解常見類
第7章 ?深入理解Audio系統(tǒng)
第一章 ?閱讀前的準(zhǔn)備工作
<span>第6章 深入理解Binder</span>
第9章 ?深入理解Vold和Rild
第2章? 深入理解JNI

第9章 ?深入理解Vold和Rild

本章主要內(nèi)容

·? 介紹Vold。

·? 介紹Rild。

本章涉及的源代碼文件名稱及位置

下面是本章分析的源碼文件名及其位置。

·? Main.cpp

system/vold/Main.cpp

·? NetlinkManager.cpp

system/vold/NetlinkManager.cpp

·? NetlinkManager.h

system/vold/NetlinkManager.h

·? NetlinkHandler.cpp

system/vold/NetlinkHandler.cpp

·? NetlinkListener.cpp

system/core/libsysutils/src/NetlinkListener.cpp

·? SocketListener.cpp

system/core/libsysutils/src/SocketListener.cpp

·? VolumeManager.cpp

system/vold/VolumeManager.cpp

·? DirectVolume.cpp

system/vold/DirectVolume.cpp

·? FrameworkListener.cpp

system/core/libsysutils/src/FrameworkListener.cpp

·? MountService.java

framework/base/services/java/com/android/server/MountService.java

·? Rild.c

hardware/ril/rild/Rild.c

·? Ril.cpp

hardware/ril/libril/Ril.cpp

·? Ril_event.h

hardware/ril/lilbril/Ril_event.h

·? Reference_ril.c

hardware/ril/reference_ril/Reference_ril.c

·? Atchannle.c

hardware/ril/reference_ril/Atchannle.c

·? Ril.h

hardware/ril/include/telephony/Ril.h

·? PhoneApp.java

package/apps/Phone/src/com/android/phone/PhoneApp.java

·? PhoneFactory.java

framework/base/telephony/java/com/android/internal/telephony/PhoneFactory.java

·? RIL.java

framework/base/telephony/java/com/android/internal/telephony/RIL.java

·? PhoneUtils.java

package/apps/Phone/src/com/android/phone/PhoneUtils.java

9.1 ?概述

本章將分析Android系統(tǒng)中兩個(gè)比較重要的程序,它們分別是:

·? Vold:Volume Daemon,用于管理和控制Android平臺(tái)外部存儲(chǔ)設(shè)備的后臺(tái)進(jìn)程,這些管理和控制,包括SD卡的插拔事件檢測(cè)、SD卡掛載、卸載、格式化等。

·? Rild:Radio Interface Layer Daemon,用于智能手機(jī)的通訊管理和控制的后臺(tái)進(jìn)程,所有和手機(jī)通訊相關(guān)的功能,例如接打電話、收發(fā)短信/彩信、GPRS等都需要Rild的參與。

Vold和Rild都是Native的程序,另外Java世界還有和它們交互的模塊,它們分別是:

·? MountService和Vold交互,一方面它可以接收來自Vold的消息,例如,在應(yīng)用程序中經(jīng)常監(jiān)聽到的ACTION_MEDIA_MOUNTED/ACTION_MEDIA_EJECT等廣播,就是由MountService根據(jù)Vold的信息而觸發(fā)的。另一方面,它可以向Vold發(fā)送控制命令,例如掛載SD卡為磁盤驅(qū)動(dòng)器的操作,就是由MountService發(fā)送命令給Vold來執(zhí)行的。

·? Phone和Rild交互,它是一個(gè)比較復(fù)雜的應(yīng)用程序。簡(jiǎn)單來說,Phone撥打電話時(shí)需要發(fā)送對(duì)應(yīng)的命令給Rild來執(zhí)行。后面在Rild的實(shí)例分析中會(huì)做相關(guān)介紹。

這兩個(gè)Daemon代碼的結(jié)構(gòu)都不算太復(fù)雜。本章將和大家一起來領(lǐng)略一下它們的風(fēng)采。

9.2 ?Vold的原理與機(jī)制分析

?Vold是Volume Daemon的縮寫,它是Android平臺(tái)中外部存儲(chǔ)系統(tǒng)的管控中心,是一個(gè)比較重要的進(jìn)程。雖然它的地位很重要,但其代碼結(jié)構(gòu)卻遠(yuǎn)沒有前面的Audio和Surface系統(tǒng)復(fù)雜。欣賞完Audio和Surface的大氣磅礴后,再來感受一下Vold的小巧玲瓏也會(huì)別有一番情趣。Vold的架構(gòu)可用圖9-1來表示:

http://wiki.jikexueyuan.com/project/deep-android-v1/images/chapter9/image001.png" alt="image" />

圖9-1 ?Vold架構(gòu)圖

從上圖中可知:

·? Vold中的NetlinkManager模塊(簡(jiǎn)稱NM)接收來自Linux內(nèi)核的uevent消息。例如SD卡的插拔等動(dòng)作都會(huì)引起Kernel向NM發(fā)送uevent消息。

·? NM將這些消息轉(zhuǎn)發(fā)給VolumeManager模塊(簡(jiǎn)稱VM)。VM會(huì)對(duì)應(yīng)做一些操作,然后把相關(guān)信息通過CommandListener(簡(jiǎn)稱CL)發(fā)送給MountService,MountService根據(jù)收到的消息會(huì)發(fā)送相關(guān)的處理命令給VM做進(jìn)一步的處理。例如待SD卡插入后,VM會(huì)將來自NM的“Disk Insert”消息發(fā)送給MountService,而后MountService則發(fā)送“Mount”指令給Vold,指示它掛載這個(gè)SD卡。

·? CL模塊內(nèi)部封裝了一個(gè)Socket用于跨進(jìn)程通信。它在Vold進(jìn)程中屬于監(jiān)聽端(即是服務(wù)端),而它的連接端(即客戶端)則是MountService。它一方面接收來自MountService的控制命令(例如卸載存儲(chǔ)卡、格式化存儲(chǔ)卡等),另一方面VM和NM模塊又會(huì)通過它,將一些信息發(fā)送給MountService。

相比于Audio和Surface系統(tǒng),Vold的架構(gòu)確實(shí)比較簡(jiǎn)單,并且Vold和MountService所在的進(jìn)程(這個(gè)進(jìn)程其實(shí)就是system_server)在進(jìn)行進(jìn)程間通信時(shí),也沒有利用Binder機(jī)制,而是直接使用了Socket,這樣,在代碼量和程序中類的派生關(guān)系上也會(huì)簡(jiǎn)單不少。

?

9.2.1 ?Netlink和Uevent的介紹

在分析Vold的代碼前,先介紹一下Linux系統(tǒng)中的Netlink和Uevent。

1. Netlink的介紹

Netlink是Linux系統(tǒng)中一種用戶空間進(jìn)程和Kernel進(jìn)行通信的機(jī)制,通過這個(gè)機(jī)制,位于用戶空間的進(jìn)程,可接收來自Kernel的一些信息(例如Vold中用到的USB或SD的插拔消息),同時(shí)應(yīng)用層也可通過Netlink向Kernel發(fā)送一些控制命令。

目前,Linux系統(tǒng)并沒有為Netlink單獨(dú)設(shè)計(jì)一套系統(tǒng)調(diào)用,而是復(fù)用了Socket的操作接口,只在創(chuàng)建Socket時(shí)會(huì)有一些特殊的地方。Netlink的具體使用方法,在進(jìn)行代碼分析時(shí)再來了解,讀者目前只需知道,通過Netlink機(jī)制應(yīng)用層,可接收來自Kernel的消息即可。

2. Uevent介紹

Uevent和Linux的Udev設(shè)備文件系統(tǒng)和設(shè)備模型有關(guān)系,它實(shí)際上就是一串字符串,字符串的內(nèi)容可告知發(fā)生了什么事情。下面通過一個(gè)實(shí)例來直觀感受Uevent:

在SD卡插入手機(jī)后(我們這里以SD卡為例),系統(tǒng)會(huì)檢測(cè)到這個(gè)設(shè)備的插入,然后內(nèi)核會(huì)通過Netlink發(fā)送一個(gè)消息給Vold,Vold將根據(jù)接收到的消息進(jìn)行處理,例如掛載這個(gè)SD卡。內(nèi)核發(fā)送的這個(gè)消息,就是Uevent,其中U代表User space(應(yīng)用層空間)。下面看SD卡插入時(shí)Vold截獲到的Uevent消息。在我的G7手機(jī)上,Uevent的內(nèi)容如下,注意,其中//號(hào)或/**/號(hào)中的內(nèi)容是為方便讀者理解而加的注釋:

[-->SD卡插入的Uevent消息]

//mmc表示MultiMedia Card,這里統(tǒng)稱為SD卡

add@/devices/platform/msm_sdcc.2/mmc_host/mmc1/mmc1:c9f2/block/mmcblk0

ACTION=add //add表示設(shè)備插入,另外還有remove和change等動(dòng)作

//DEVPATH表示該設(shè)備位于/sys目錄中的設(shè)備路徑

DEVPATH=/devices/platform/msm_sdcc.2/mmc_host/mmc1/mmc1:c9f2/block/mmcblk0?

/*

SUBSYSTEM表示該設(shè)備屬于哪一類設(shè)備,block為塊設(shè)備,磁盤也屬于這一類設(shè)備,另外還有

character(字符)設(shè)備等類型。

*/

SUBSYSTEM=block

MAJOR=179//MAJOR和MINOR分別表示該設(shè)備的主次設(shè)備號(hào),二者聯(lián)合起來可以標(biāo)識(shí)一個(gè)設(shè)備?

MINOR=0

DEVNAME=mmcblk0?

DEVTYPE=disk//設(shè)備Type為disk????

NPARTS=3 //這個(gè)表示該SD卡上的分區(qū),我的SD卡上有三塊分區(qū)

SEQNUM=1357//序號(hào)

由于我的SD卡上還有分區(qū),所以還會(huì)接收到和分區(qū)相關(guān)的Uevent。簡(jiǎn)單看一下:

[-->SD卡插入后和分區(qū)相關(guān)的Uevent消息]

add@/devices/platform/msm_sdcc.2/mmc_host/mmc1/mmc1:c9f2/block/mmcblk0/mmcblk0p1??

ACTION=add

//比上面那個(gè)DEVPATH多了一個(gè)mmcblk0p1

DEVPATH=/devices/platform/msm_sdcc.2/mmc_host/mmc1/mmc1:c9f2/block/mmcblk0/mmcblk0p1???????

SUBSYSTEM=block?

MAJOR=179???????

MINOR=1

DEVNAME=mmcblk0p1???????

DEVTYPE=partition //設(shè)備類型變?yōu)閜artition,表示分區(qū)?

PARTN=1

SEQNUM=1358

通過上面實(shí)例,我們和Uevent來了一次親密接觸,具體到Vold,也就是內(nèi)核通過Uevent告知外部存儲(chǔ)系統(tǒng)發(fā)生了哪些事情,那么Uevent在什么情況下會(huì)由Kernel發(fā)出呢?

·? 當(dāng)設(shè)備發(fā)生變化時(shí),這會(huì)引起Kernel發(fā)送Uevent消息,例如設(shè)備的插入和拔出等。如果Vold在設(shè)備發(fā)生變化之前已經(jīng)建立了Netlink IPC通信,那么Vold可以接收到這些Uevent消息。這種情況是由設(shè)備發(fā)生變化而觸發(fā)的。

·? 設(shè)備一般在/sys對(duì)應(yīng)的目錄下有一個(gè)叫uevent的文件,往該文件中寫入指定的數(shù)據(jù),也會(huì)觸Kernel發(fā)送和該設(shè)備相關(guān)的Uevent消息,這是由應(yīng)用層觸發(fā)的。例如Vold啟動(dòng)時(shí),會(huì)往這些uevent文件中寫數(shù)據(jù),通過這種方式促使內(nèi)核發(fā)送Uevent消息,這樣Vold就能得到這些設(shè)備的當(dāng)前信息了。

根據(jù)上面介紹可知,Netlink和Uevent的目的,就是讓Vold隨時(shí)獲悉外部存儲(chǔ)系統(tǒng)的信息,這至關(guān)重要。我們總不會(huì)希望發(fā)生諸如SD卡都被拔了,而Vold卻一無所知的情況吧?

9.2.2 ?初識(shí)Vold

下面來認(rèn)識(shí)一下Vold,它的代碼在main.cpp中,如下所示:

[-->Main.cpp]

int main() {

?

???VolumeManager *vm;

???CommandListener *cl;

???NetlinkManager *nm;

??

???SLOGI("Vold 2.1 (the revenge) firing up");

??? //創(chuàng)建文件夾/dev/block/vold

???mkdir("/dev/block/vold", 0755);

???

//①創(chuàng)建VolumeManager對(duì)象

??? if(!(vm = VolumeManager::Instance())) {

???????SLOGE("Unable to create VolumeManager");

???????exit(1);

??? };

?? //②創(chuàng)建NetlinkManager對(duì)象

??? if(!(nm = NetlinkManager::Instance())) {

???????SLOGE("Unable to create NetlinkManager");

???????exit(1);

??? };

?

??? //③創(chuàng)建CommandListener對(duì)象

cl = new CommandListener();

?

???vm->setBroadcaster((SocketListener *) cl);

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

??? //④啟動(dòng)VM

??? if(vm->start()) {

??????? ......

???????exit(1);

??? }

?? //⑤根據(jù)配置文件來初始化VM

??? if(process_config(vm)) {

??????? ......

??? }

?? //⑥啟動(dòng)NM

??? if(nm->start()) {

??????? ......

???????exit(1);

??? }

??

//通過往/sys/block目錄下對(duì)應(yīng)的uevent文件寫”add\n”來觸發(fā)內(nèi)核發(fā)送Uevent消息

???? coldboot("/sys/block");

??? {

???????FILE *fp;

???????char state[255];

?????? /*

?????????Android支持將手機(jī)上的外部存儲(chǔ)設(shè)備作為磁盤掛載到電腦上。下面的代碼可查看是否打開了

???????? 磁盤掛載功能。這里涉及UMS(Usb Mass Storage,USB大容量存儲(chǔ))方面的知識(shí)。

*/

??????? if((fp = fopen("/sys/devices/virtual/switch/usb_mass_storage/state",

???????????????????????? "r"))) {

???????????if (fgets(state, sizeof(state), fp)) {

???????????????if (!strncmp(state, "online", 6)) {

?????????????????? //⑧VM通過CL向感興趣的模塊(如MountService)通知UMS的狀態(tài)

???????? ???????????vm->notifyUmsConnected(true);

???????????????} else {

???????????????????vm->notifyUmsConnected(false);

???????????????}

???????????}

???????????......

???????????fclose(fp);

??????? }

......

??? }

?????......

? //⑨啟動(dòng)CL

?? if(cl->startListener()) {

??????? ......

???????exit(1);

??? }

?? //無限循環(huán)

???while(1) {

???????sleep(1000);

??? }

?

???SLOGI("Vold exiting");

???exit(0);

}

上面代碼中列出了九個(gè)關(guān)鍵點(diǎn)。由于Vold將其功能合理分配到了各個(gè)模塊中,所以這九個(gè)關(guān)鍵點(diǎn)將放到圖9-1所示Vold的三個(gè)模塊中去討論。

下面,看第一個(gè)模塊NetlinkManager,簡(jiǎn)稱NM。

9.2.3 ?NetlinkManager模塊的分析

在Vold代碼中,使用NM模塊的流程是:

·? 調(diào)用Instance創(chuàng)建一個(gè)NM對(duì)象。

·? 調(diào)用setBroadcaster設(shè)置CL對(duì)象。

·? 調(diào)用start啟動(dòng)NM。

接下來,按這三個(gè)步驟來分析NM模塊。

1. 創(chuàng)建NM

Vold調(diào)用Instance函數(shù)創(chuàng)建了一個(gè)NM對(duì)象。看到Instance這個(gè)函數(shù),讀者應(yīng)能想到,這里可能是采用了單例模式。來看是否如此,代碼如下所示。

[-->NetlinkManager.cpp]

NetlinkManager *NetlinkManager::Instance() {

??? if(!sInstance)

???????sInstance = new NetlinkManager();//果然是單例模式

??? returnsInstance;

}

NM的創(chuàng)建真是非常簡(jiǎn)單。再看第二個(gè)被調(diào)用的函數(shù)setBroadcaster。

2. setBroadcaster的分析

setBroadcaster就更簡(jiǎn)單了,它的實(shí)現(xiàn)在NetlinkManger類的聲明中,如下所示:

[-->NetlinkManager.h]

void setBroadcaster(SocketListener *sl)

setBroadcaster參數(shù)中的那個(gè)sl其實(shí)際類型為CommandListener。需要說明的是,雖然NM設(shè)置了CL對(duì)象,但Vold的NM并沒有通過CL發(fā)送消息和接收命令,所以在圖9-1中,NM模塊和CL模塊并沒有連接線,這一點(diǎn)務(wù)請(qǐng)注意。

下面看最后一個(gè)函數(shù)start。

3. start的分析

前面說過,NM模塊將使用Netlink和Kernel進(jìn)行IPC通信,那么它是怎么做到的呢?來看代碼,如下所示:

[-->NetlinkManager.cpp]

int NetlinkManager::start() {

?? //PF_NETLINK使用的socket地址結(jié)構(gòu)是sockaddr_nl,而不是一般的sockaddr_in

??? structsockaddr_nl nladdr;

??? int sz= 64 * 1024;

?

???memset(&nladdr, 0, sizeof(nladdr));

???nladdr.nl_family = AF_NETLINK;

???nladdr.nl_pid = getpid(); //設(shè)置自己的進(jìn)程pid

???nladdr.nl_groups = 0xffffffff;

???

/*

創(chuàng)建PF_NETLINK地址簇的socket,目前只支持SOCK_DGRAM類型,第三個(gè)參數(shù)

NETLINK_KOBJECT_UEVENT表示要接收內(nèi)核的Uevent事件。

?? */

??? if((mSock = socket(PF_NETLINK,

???????????????????????SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {

??????? ......

???????return -1;

??? }

??? //設(shè)置Socket接收緩沖區(qū)大小

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

??????? ......

???????return -1;

??? }

??? //必須對(duì)該socket執(zhí)行bind操作

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

??????? ......

???????return -1;

??? }

?? //創(chuàng)建一個(gè)NetlinkHandler對(duì)象,并把創(chuàng)建好的Socket句柄傳給它。

mHandler = new NetlinkHandler(mSock);

//調(diào)用NetlinkHandler對(duì)象的start

??? if(mHandler->start()) {

???????SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));

???????return -1;

??? }

??? return0;

}

從代碼上看,NM的start函數(shù)分為兩個(gè)步驟:

·? 創(chuàng)建地址簇為PF_NETLINK類型的socket并做一些設(shè)置,這樣NM就能和Kernel通信了。關(guān)于Netlink的使用技巧網(wǎng)上有很多資料,讀者可在Linux系統(tǒng)上通過man netlink命令來查詢相關(guān)信息。

·? 創(chuàng)建NetlinkHandler對(duì)象,并調(diào)用它的start??磥?,后續(xù)工作都是由NetlinkHandler來完成的。

據(jù)上文分析可看出,NetlinkHandler才是真正的主角,下面就來分析它。為書寫方便起見,NetlinkHandler簡(jiǎn)稱為NLH。

4. NetlinkHandler的分析

(1)創(chuàng)建NLH

代碼結(jié)構(gòu)簡(jiǎn)單的Vold程序中,NetlinkHandler卻有一個(gè)相對(duì)不簡(jiǎn)單的派生關(guān)系,如圖9-2所示:

http://wiki.jikexueyuan.com/project/deep-android-v1/images/chapter9/image002.png" alt="image" />

圖9-2 ?NLH的派生關(guān)系圖

直接看代碼,來認(rèn)識(shí)這個(gè)NLH:

[-->NetlinkHandler.cpp]

NetlinkHandler::NetlinkHandler(int listenerSocket):

???????????????NetlinkListener(listenerSocket) {

?//調(diào)用基類NetlinkListener的構(gòu)造函數(shù)。注意傳入的參數(shù)是和Kernel通信的socket

//句柄。注意,文件描述符和句柄表示的是同一個(gè)東西,這里不再區(qū)分二者。

}

再看基類NetlinkListener的構(gòu)造函數(shù):

[-->NetlinkListener.cpp]

NetlinkListener::NetlinkListener(int socket) :

???????????????????????????SocketListener(socket, false) {

?//調(diào)用基類SocketListener的構(gòu)造函數(shù),第二個(gè)參數(shù)為false。

}

基類SocketListener的構(gòu)造函數(shù)是:

[-->SocketListener.cpp]

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

???mListen = listen; //這個(gè)參數(shù)是false

???mSocketName = NULL;

mSock = socketFd;//保存和Kernel通信的socket描述符

//初始化一個(gè)mutex,看來會(huì)有多個(gè)線程存在

pthread_mutex_init(&mClientsLock, NULL);

/*

SocketClientCollection的聲明如下,它是一個(gè)列表容器。

typedef android::List<SocketClient *>SocketClientCollection

其中,SocketClient代表和Socket服務(wù)端通信的客戶端。

*/

??? ??mClients = new SocketClientCollection();

}

NLH的創(chuàng)建分析完了。此過程中沒有什么新鮮內(nèi)容。下面看它的start函數(shù)。

本章內(nèi)容會(huì)大量涉及Socket,所以讀者應(yīng)先了解與Socket有關(guān)的知識(shí),如果需要深入研究,建議閱讀《Unix NetworkingProgramming Volume I》一書。

(2)start的分析

在分析前面的代碼時(shí),曾看到NetlinkHandler會(huì)創(chuàng)建一個(gè)同步互斥對(duì)象,這表明NLH會(huì)在多線程環(huán)境中使用,那么這個(gè)線程會(huì)在哪里創(chuàng)建呢?來看start的代碼,如下所示:

[-->NetlinkHandler.cpp]

int NetlinkHandler::start() {

??? returnthis->startListener();//startListener由SocketListener實(shí)現(xiàn)。

}

[-->SocketListener.cpp]

int SocketListener::startListener() {

?

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

???????errno = EINVAL;

???????return -1;

??? } elseif (mSocketName) {

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

??????????return -1;

??????? }

??? }

/*

還記得構(gòu)造NLH時(shí)的參數(shù)嘛?mListen為false,這表明NLH不是監(jiān)聽端(listen)。

這里為了代碼和操作的統(tǒng)一,用mSock做參數(shù)構(gòu)造了一個(gè)SocketClient對(duì)象,

并加入到mClients列表中,但這個(gè)SocketClient并不是真實(shí)客戶端的代表。

*/

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

??????? ......

???????return -1;

} else if (!mListen)//以mSock為參數(shù)構(gòu)造SocketClient對(duì)象,并加入到對(duì)應(yīng)列表中

???????mClients->push_back(new SocketClient(mSock));

/*

pipe系統(tǒng)調(diào)用將創(chuàng)建一個(gè)匿名管道,mCtrlPipe是一個(gè)int類型的二元數(shù)組。

其中mCtrlPipe[0]用于從管道讀數(shù)據(jù),mCtrlPipe[1]用于往管道寫數(shù)據(jù)

*/

??? if(pipe(mCtrlPipe)) {

??????? ......

???????return -1;

??? }

?? //創(chuàng)建一個(gè)工作線程,線程函數(shù)是threadStart。

??? if(pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {

????? ??......

???????return -1;

??? }

?

??? return0;

}

如果熟悉Socket編程,理解上面的代碼就非常容易了。下面來看NLH的工作線程。

(3)工作線程的分析

工作線程的線程函數(shù)threadStart的代碼如下所示:

[-->SocketListener.cpp]

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

???SocketListener *me = reinterpret_cast<SocketListener *>(obj);

?

???me->runListener();//調(diào)用runListener。

???pthread_exit(NULL);

??? returnNULL;

}

//直接分析runListener

void SocketListener::runListener() {

?

???while(1) {

???????SocketClientCollection::iterator it;

???????fd_set read_fds;

???????int rc = 0;

???????int max = 0;

?

???????FD_ZERO(&read_fds);

?

??????? if(mListen) {//mListen為false,所以不走這個(gè)if分支

???????????max = mSock;

???????????FD_SET(mSock, &read_fds);

??????? }

?????? /*

計(jì)算max,為什么要有這個(gè)操作?這是由select函數(shù)決定的,它的第一個(gè)參數(shù)的取值

必須為它所監(jiān)視的文件描述符集合中最大的文件描述符加1。

? ????*/

???????FD_SET(mCtrlPipe[0], &read_fds);

??????? if(mCtrlPipe[0] > max)

???????????max = mCtrlPipe[0];

??????? //還是計(jì)算fd值最大的那個(gè)

???????pthread_mutex_lock(&mClientsLock);

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

????????? ??FD_SET((*it)->getSocket(), &read_fds);

???????????if ((*it)->getSocket() > max)

???????????????max = (*it)->getSocket();

??????? }

???????pthread_mutex_unlock(&mClientsLock);

??????? /*

注意select函數(shù)的第一個(gè)參數(shù),為max+1。讀者可以通過man select來查詢

????????? select的用法,注意,在Windows平臺(tái)上的select對(duì)第一個(gè)參數(shù)沒有要求。

??????? */

??????? if((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {

???????????sleep(1);

???????????continue;

??????? }else if (!rc)

???????????continue;

??????? //如果管道可讀的話,表示需要退出工作線程。

??????? if(FD_ISSET(mCtrlPipe[0], &read_fds))

???????????break;

??????? if(mListen && FD_ISSET(mSock, &read_fds)) {

???????????//如果是listen端的話,mSock可讀表示有客戶端connect上

???????????struct sockaddr addr;

???????????socklen_t alen = sizeof(addr);

???????????int c;

????????????//調(diào)用accept接受客戶端的連接,返回用于和客戶端通信的Socket描述符

???????????if ((c = accept(mSock, &addr, &alen)) < 0) {

???????????????SLOGE("accept failed (%s)", strerror(errno));

???????????????sleep(1);

???????????????continue;

???????????}

???????????pthread_mutex_lock(&mClientsLock);

???????????//根據(jù)返回的客戶端Socket描述符構(gòu)造一個(gè)SocketClient對(duì)象,并加入到對(duì)應(yīng)list

???????????mClients->push_back(new SocketClient(c));

???????????pthread_mutex_unlock(&mClientsLock);

??????? }

?

??????? do{

???????????pthread_mutex_lock(&mClientsLock);

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

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

???????????????if (FD_ISSET(fd, &read_fds)) {

???????????????????pthread_mutex_unlock(&mClientsLock);

????????????????/*

有數(shù)據(jù)通過Socket發(fā)送過來,所以調(diào)用onDataAvailable進(jìn)行處理。

如果在onDataAvailable返回false,表示需要關(guān)閉該連接。

????????????????*/

??????????????????? if (!onDataAvailable(*it)){

??????????????????????? close(fd);

???????????????????????pthread_mutex_lock(&mClientsLock);

??????????????????????? delete *it;

??????????????????????? it =mClients->erase(it);

???????????????????????pthread_mutex_unlock(&mClientsLock);

??????????????????? }

??????????????????? FD_CLR(fd, &read_fds);

??????????????????? continue;

???????????????}

???????????}

???????????pthread_mutex_unlock(&mClientsLock);

??????? }while (0);

??? }

}

從代碼中可看到:

·? 工作線程退出的條件是匿名管道可讀,但在一般情況下不需要它退出,所以可以忽略此項(xiàng)內(nèi)容。

·? 不論是服務(wù)端還是客戶端,收到數(shù)據(jù)后都會(huì)調(diào)用onDataAvailable進(jìn)行處理。

下面就來看NLH的數(shù)據(jù)處理。

(4)數(shù)據(jù)處理

根據(jù)前面的分析,收到數(shù)據(jù)后首先調(diào)用onDataAvailable函數(shù)進(jìn)行處理,這個(gè)函數(shù)由NLH的基類NetlinkListener實(shí)現(xiàn)。代碼如下所示:

[-->NetlinkListener]

bool NetlinkListener::onDataAvailable(SocketClient*cli)

{

??? intsocket = cli->getSocket();

??? intcount;

???

/*

調(diào)用recev接收數(shù)據(jù),如果接收錯(cuò)誤,則返回false,這樣這個(gè)socket在

上面的工作線程中就會(huì)被close。

*/

??? if((count = recv(socket, mBuffer, sizeof(mBuffer), 0)) < 0) {

???????SLOGE("recv failed (%s)", strerror(errno));

??????? return false;

??? }

?? //new一個(gè)NetlinkEvent,并調(diào)用decode來解析接收到的Uevent數(shù)據(jù)

???NetlinkEvent *evt = new NetlinkEvent();

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

??????? ?goto out;

??? }

??? //調(diào)用onEvent,并傳遞NetlinkEvent對(duì)象。

???onEvent(evt);

out:

??? deleteevt;

return true;

decode函數(shù)就是將收到的Uevent信息填充到一個(gè)NetlinkEvent對(duì)象中,例如Action是什么,SUBSYSTEM是什么等,以后處理Uevent時(shí)就不用再解析字符串了。

看onEvent函數(shù),此函數(shù)是由NLH自己實(shí)現(xiàn)的,代碼如下所示:

[-->NetlinkHandler.cpp]

void NetlinkHandler::onEvent(NetlinkEvent *evt){

???VolumeManager *vm = VolumeManager::Instance();

??? constchar *subsys = evt->getSubsystem();

?

??? if(!subsys) {

???????return;

??? }

??

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

???????vm->handleBlockEvent(evt); //調(diào)用VM的handleBlockEvent

??? } elseif (!strcmp(subsys, "switch")) {

???????vm->handleSwitchEvent(evt);//調(diào)用VM的handleSwitchEvent

} else if (!strcmp(subsys, "battery")){

???? ?//這兩個(gè)事件和外部存儲(chǔ)系統(tǒng)沒有關(guān)系,所以不處理

??? } elseif (!strcmp(subsys, "power_supply")) {

??? }

}

NLH的工作已介紹完,下面總結(jié)一下NM模塊的工作。

5. NM模塊的總結(jié)

NM模塊的功能就是從Kernel接收Uevent消息,然后轉(zhuǎn)換成一個(gè)NetlinkEvent對(duì)象,最后會(huì)調(diào)用VM的處理函數(shù)來處理這個(gè)NetlinkEvent對(duì)象。

9.2.4 ?VolumeManager模塊的分析

Vold使用VM模塊的流程是:

·? 調(diào)用Instance創(chuàng)建一個(gè)VM對(duì)象。

·? 調(diào)用setBroadcaster設(shè)置CL對(duì)象,這個(gè)函數(shù)和NM的setBroadcaster一樣,所以本節(jié)不再介紹它。

·? 調(diào)用start啟動(dòng)VM。

·? 調(diào)用process_config配置VM。

現(xiàn)在來看除setBroadcaster之外的三個(gè)函數(shù)。

1. 創(chuàng)建VM和start的分析

VM的創(chuàng)建及start函數(shù)都非常簡(jiǎn)單,代碼如下所示。

[-->VolumeManager.cpp]

VolumeManager *VolumeManager::Instance() {

??? if(!sInstance)

???????sInstance = new VolumeManager();

??? returnsInstance;

}

可以看到,VM也采用了單例的模式,所以全進(jìn)程只會(huì)存在一個(gè)VM對(duì)象。

下面看VM的start:

[-->VolumeManager.cpp]

int VolumeManager::start() {

??? return 0;

}

start很簡(jiǎn)單,沒有任何操作。

2. process_config的分析

process_config函數(shù)會(huì)根據(jù)配置文件配置VM對(duì)象,其代碼如下所示:

[-->Main.cpp]

static int process_config(VolumeManager *vm) {

??? FILE*fp;

??? int n= 0;

??? charline[255];

?? //讀取/etc/vold.fstab文件

??? if(!(fp = fopen("/etc/vold.fstab", "r"))) {

???????return -1;

??? }

?

???while(fgets(line, sizeof(line), fp)) {

???????char *next = line;

???????char *type, *label, *mount_point;

?

???????n++;

???????line[strlen(line)-1] = '\0';

?

??????? if(line[0] == '#' || line[0] == '\0')

???????????continue;

?

??????? if(!(type = strsep(&next, " \t"))) {

???????????goto out_syntax;

??????? }

??????? if(!(label = strsep(&next, " \t"))) {

???????????goto out_syntax;

??????? }

??????? if(!(mount_point = strsep(&next, " \t"))) {

??????? ????goto out_syntax;

??????? }

?

??????? if(!strcmp(type, "dev_mount")) {

???????????DirectVolume *dv = NULL;

???????????char *part, *sysfs_path;

?

???????????if (!(part = strsep(&next, " \t"))) {

???????????????......

???????????????goto out_syntax;

??????????? }

???????????if (strcmp(part, "auto") && atoi(part) == 0) {

???????????????goto out_syntax;

???????????}

?

???????????if (!strcmp(part, "auto")) {

??????????????//①構(gòu)造一個(gè)DirectVolume對(duì)象

???????????????dv = new DirectVolume(vm, label, mount_point, -1);

???????????} else {

???????????????dv = new DirectVolume(vm, label, mount_point, atoi(part));

???????????}

?

???????????while((sysfs_path = strsep(&next, " \t"))) {

??????????????? //②添加設(shè)備路徑

???????????????if (dv->addPath(sysfs_path)) {

??????????????????? ......

??????????????????? goto out_fail;

???????????????}

???????????}

??????????//為VolumeManager對(duì)象增加一個(gè)DirectVolume對(duì)象

???????????vm->addVolume(dv);

??????? }

......

??? }

? ......

??? return-1;??

}

從上面代碼中發(fā)現(xiàn),process_config的主要功能就是解析/etc/vold.fstab。這個(gè)文件的作用和Linux系統(tǒng)中的fstab文件很類似,就是設(shè)置一些存儲(chǔ)設(shè)備的掛載點(diǎn),我的HTC G7手機(jī)上這個(gè)文件的內(nèi)容如圖9-3所示:

http://wiki.jikexueyuan.com/project/deep-android-v1/images/chapter9/image003.png" alt="image" />

圖9-3 ?我的手機(jī)上vold.fstab內(nèi)容

從上圖的紅框中可知:

·? sdcard為volume的名字。

·? /mnt/sdcard表示mount的位置。

·? 1表示使用存儲(chǔ)卡上的第一個(gè)分區(qū),auto表示沒有分區(qū)。現(xiàn)在有很多定制的ROM要求SD卡上存在多個(gè)分區(qū)。

·? /devices/xxxx等內(nèi)容表示MMC設(shè)備在sysfs中的位置。

根據(jù)G7的vold.fstab文件,可以構(gòu)造一個(gè)DirectVolume對(duì)象。

注意,根據(jù)手機(jī)刷的ROM的不同,vold.fstab文件會(huì)有較大差異。

3. DirectVolume的分析

DirectVolume從Volume類派生,可把它看成是一個(gè)外部存儲(chǔ)卡(例如一張SD卡)在代碼中的代表物。它封裝了對(duì)外部存儲(chǔ)卡的操作,例如加載/卸載存儲(chǔ)卡、格式化存儲(chǔ)卡等。

下面是process_config函數(shù)中和DirectVolume相關(guān)的地方:

·? 一個(gè)是創(chuàng)建DirectVolume。

·? 另一個(gè)是調(diào)用DirectVolume的addpath函數(shù)。

它們的代碼如下所示:

[-->DirectVolume.cpp]

DirectVolume::DirectVolume(VolumeManager *vm,const char *label,

?????????????????????????? const char*mount_point, int partIdx) :

???? ?????????Volume(vm, label, mount_point) {//初始化基類

?? /*

????? 注意其中的參數(shù):

???? label為”sdcard”,mount_point為”/mnt/sdcard”,partIdx為1????

*/

???mPartIdx = partIdx;

//PathCollection定義為typedef android::List<char *> PathCollection

//其實(shí)就是一個(gè)字符串list

??? mPaths= new PathCollection();

??? for(int i = 0; i < MAX_PARTITIONS; i++)

???????mPartMinors[i] = -1;

???mPendingPartMap = 0;

???mDiskMajor = -1;? //存儲(chǔ)設(shè)備的主設(shè)備號(hào)

???mDiskMinor = -1;? //存儲(chǔ)設(shè)備的次設(shè)備號(hào),一個(gè)存儲(chǔ)設(shè)備將由主次兩個(gè)設(shè)備號(hào)標(biāo)識(shí)。

???mDiskNumParts = 0;

?? //設(shè)置狀態(tài)為NoMedia

???setState(Volume::State_NoMedia);

}

//再來看addPath函數(shù),它主要目的是添加設(shè)備在sysfs中的路徑,G7的vold.fstab上有兩個(gè)路

//徑,見圖9-3中的最后一行。

int DirectVolume::addPath(const char *path) {

???mPaths->push_back(strdup(path));

??? return0;

}

這里簡(jiǎn)單介紹一下addPath的作用。addPath把和某個(gè)存儲(chǔ)卡接口相關(guān)的設(shè)備路徑與這個(gè)DirectVolume綁定到一起,并且這個(gè)設(shè)備路徑和Uevent中的DEVPATH是對(duì)應(yīng)的,這樣就可以根據(jù)Uevent的DEVPATH找到是哪個(gè)存儲(chǔ)卡的DirectVolume發(fā)生了變動(dòng)。當(dāng)然手機(jī)上目前只有一個(gè)存儲(chǔ)卡接口,所以Vold也只有一個(gè)DirectVolume。

4. NM和VM交互

在分析NM模塊的數(shù)據(jù)處理時(shí)發(fā)現(xiàn),NM模塊接收到Uevent事件后,會(huì)調(diào)用VM模塊進(jìn)行處理,下面來看這塊的內(nèi)容。

先回顧一下NM調(diào)用VM模塊的地方,代碼如下所示:

[-->NetlinkHandler.cpp]

void NetlinkHandler::onEvent(NetlinkEvent *evt){

???VolumeManager *vm = VolumeManager::Instance();

??? constchar *subsys = evt->getSubsystem();

......

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

???????vm->handleBlockEvent(evt); //調(diào)用VM的handleBlockEvent

??? } elseif (!strcmp(subsys, "switch")) {

???????vm->handleSwitchEvent(evt);//調(diào)用VM的handleSwitchEvent

}

......

}

在上面代碼中,如果Uevent是block子系統(tǒng),則調(diào)用handleBlockEvent;如果是switch,則調(diào)用handleSwitchEvent。handleSwitchEvent主要處理SD卡掛載磁盤的通知,比較簡(jiǎn)單。這里只分析handleBlockEvent事件。

[-->VolumeManager.cpp]

void VolumeManager::handleBlockEvent(NetlinkEvent*evt) {

??? constchar *devpath = evt->findParam("DEVPATH");

?

/*

前面在process_config中構(gòu)造的DirectVolume對(duì)象保存在了mVolumes中,它的定義如下:

typedef android::List<Volume *>VolumeCollection,也是一個(gè)列表。

??注意它保存的是Volume指針,而我們的DirectVolume是從Volume派生的

*/

???VolumeCollection::iterator it;

??? boolhit = false;

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

??????? //調(diào)用每個(gè)Volume的handleBlockEvent事件,就我的G7手機(jī)而言,實(shí)際上將調(diào)用

????? ??//DirectVolume的handleBlockEvent函數(shù)。

??????? if(!(*it)->handleBlockEvent(evt)) {

???????????hit = true;

???????????break;

??????? }

??? }

}

NM收到Uevent消息后,DirectVolume也將應(yīng)聲而動(dòng),它的handleBlockEvent的處理是:

[-->DirectVolume.cpp]

int DirectVolume::handleBlockEvent(NetlinkEvent*evt) {

??? constchar *dp = evt->findParam("DEVPATH");

?

PathCollection::iterator? it;

//將Uevent的DEVPATH和addPath添加的路徑進(jìn)行對(duì)比,判斷屬不屬于自己管理的范圍。

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

?????????????????//創(chuàng)建設(shè)備節(jié)點(diǎn)

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

??????????????????? ......

???????????????}

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

??????????????????? handleDiskAdded(dp, evt);//添加一個(gè)磁盤

???????????????} else {

??????????????????? /*

對(duì)于有分區(qū)的SD卡,先收到上面的“disk”消息,然后每個(gè)分區(qū)就會(huì)收到

?????????????????? 一個(gè)分區(qū)添加消息。

?????????????????? */

??????????????????? handlePartitionAdded(dp,evt);

???????????????}

???????????} else if (action == NetlinkEvent::NlActionRemove) {

???? ???????????......

???????????} else if (action == NetlinkEvent::NlActionChange) {

??????????????......

???????????}

???????????......

???????????return 0;

??????? }

??? }

??? errno= ENODEV;

??? return-1;

}

關(guān)于DirectVolume針對(duì)不同Uevent的具體處理方式,后面將通過一個(gè)SD卡插入案例來分析。

5. VM模塊的總結(jié)

從前面的代碼分析中可知,VM模塊的主要功能是管理Android系統(tǒng)中的外部存儲(chǔ)設(shè)備。圖9-4描述了VM模塊的功能:

http://wiki.jikexueyuan.com/project/deep-android-v1/images/chapter9/image004.png" alt="image" />

圖9-4? VM模塊的職責(zé)

通過對(duì)上圖和前面代碼的分析可知:

·? SD卡的變動(dòng)(例如熱插拔)將導(dǎo)致Kernel發(fā)送Uevent消息給NM模塊。

·? NM模塊調(diào)用VM模塊處理這些Uevent消息。

·? VM模塊遍歷它所持有的Volume對(duì)象,Volume對(duì)象根據(jù)addPath添加的DEVPATH和Uevent消息中的DEVPATH來判斷,自己是否可以處理這個(gè)消息。

至于Volume到底如何處理Uevent消息,將通過一個(gè)實(shí)例來分析。

9.2.5 關(guān)于 CommandListener模塊的分析

Vold使用CL模塊的流程是:

·? 使用new創(chuàng)建一個(gè)CommandListener對(duì)象;

·? 調(diào)用CL的startListener函數(shù)。

來看這兩個(gè)函數(shù)。

1. 創(chuàng)建CommandListener的分析

和NetlinkerHandler一樣,CommandListener也有一個(gè)相對(duì)不簡(jiǎn)單的派生關(guān)系,它的家族圖譜如圖9-5所示:

http://wiki.jikexueyuan.com/project/deep-android-v1/images/chapter9/image005.png" alt="image" />

圖9-5? CommandListener家族圖譜

根據(jù)上圖可以知道:

·? CL定義了一些和Command相關(guān)的內(nèi)部類,這里采用了設(shè)計(jì)模式中的Command模式,每個(gè)命令的處理函數(shù)都是runCommand。注意,上圖只列出了部分Command類。

·? CL也是從SocketListener派生的,不過它是Socket的監(jiān)聽(listen)端。

下面看它的代碼:

[-->CommandListener.cpp]

CommandListener::CommandListener() :

????????????????FrameworkListener("vold") {

??? //CL模塊支持的命令

???registerCmd(new DumpCmd());

???registerCmd(new VolumeCmd());

???registerCmd(new AsecCmd());

???registerCmd(new ShareCmd());

???registerCmd(new StorageCmd());

???registerCmd(new XwarpCmd());

}

/*

registerCmd函數(shù)將Command保存到mCommands中,mCommands的定義為一個(gè)列表,如下:

typedef android::List<FrameworkCommand *>FrameworkCommandCollection;

*/

voidFrameworkListener::registerCmd(FrameworkCommand *cmd) {

???mCommands->push_back(cmd);

}

從上面的代碼可知,CommandListener的基類是FrameworkListener,而FrameworkListener又從SocketListener類派生。之前在分析NM模塊的NetLinkerHandler時(shí),已介紹過SocketListener相關(guān)的知識(shí)了,所以此處不再贅述,只總結(jié)一下CL創(chuàng)建后的結(jié)果,它們是:

·? CL會(huì)創(chuàng)建一個(gè)監(jiān)聽端的socket,這樣就可以接收客戶端的鏈接。

·? 客戶端發(fā)送命令給CL,CL則從mCommands中找到對(duì)應(yīng)的命令,并交給該命令的runCommand函數(shù)處理。

下面來關(guān)注第二個(gè)函數(shù)startListener,這個(gè)函數(shù)由SocketListener實(shí)現(xiàn)。

2. startListener的分析和數(shù)據(jù)處理

其實(shí)在分析NetlinkerHandler時(shí),已經(jīng)介紹了startListener函數(shù),這里再簡(jiǎn)單回顧一下,有些具體內(nèi)容和本章對(duì)NetlinkerHandler的分析有關(guān)。

[-->SocketListener.cpp]

int SocketListener::startListener() {

?

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

??????? ......

???????errno = EINVAL;

???????return -1;

} else if (mSocketName) {

???/