本章主要內(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
本章將分析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)采。
?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)單不少。
?
在分析Vold的代碼前,先介紹一下Linux系統(tǒng)中的Netlink和Uevent。
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的消息即可。
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卻一無所知的情況吧?
下面來認(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。
在Vold代碼中,使用NM模塊的流程是:
·? 調(diào)用Instance創(chuàng)建一個(gè)NM對(duì)象。
·? 調(diào)用setBroadcaster設(shè)置CL對(duì)象。
·? 調(diào)用start啟動(dòng)NM。
接下來,按這三個(gè)步驟來分析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。
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。
前面說過,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。
代碼結(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》①一書。
在分析前面的代碼時(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的工作線程。
工作線程的線程函數(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ù)處理。
根據(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模塊的工作。
NM模塊的功能就是從Kernel接收Uevent消息,然后轉(zhuǎn)換成一個(gè)NetlinkEvent對(duì)象,最后會(huì)調(diào)用VM的處理函數(shù)來處理這個(gè)NetlinkEvent對(duì)象。
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ù)。
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)單,沒有任何操作。
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ì)有較大差異。
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。
在分析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卡插入案例來分析。
從前面的代碼分析中可知,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í)例來分析。
Vold使用CL模塊的流程是:
·? 使用new創(chuàng)建一個(gè)CommandListener對(duì)象;
·? 調(diào)用CL的startListener函數(shù)。
來看這兩個(gè)函數(shù)。
和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)。
其實(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) {
???/