Vold使用VM模塊的流程是:
- 調用Instance創建一個VM對象。
- 調用setBroadcaster設置CL對象,這個函數和NM的setBroadcaster一樣,所以本節不再介紹它。
- 調用start啟動VM。
- 調用process_config配置VM。
現在來看除setBroadcaster之外的三個函數。
1. 創建VM和start的分析
VM的創建及start函數都非常簡單,代碼如下所示。
**VolumeManager.cpp**
~~~
VolumeManager *VolumeManager::Instance() {
if(!sInstance)
sInstance = new VolumeManager();
returnsInstance;
}
~~~
可以看到,VM也采用了單例的模式,所以全進程只會存在一個VM對象。
下面看VM的start:
**VolumeManager.cpp**
~~~
int VolumeManager::start() {
return 0;
}
~~~
start很簡單,沒有任何操作。
2. process_config的分析
process_config函數會根據配置文件配置VM對象,其代碼如下所示:
**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")) {
//①構造一個DirectVolume對象
dv = new DirectVolume(vm, label, mount_point, -1);
} else {
dv = new DirectVolume(vm, label, mount_point, atoi(part));
}
while((sysfs_path = strsep(&next, " \t"))) {
//②添加設備路徑
if (dv->addPath(sysfs_path)) {
......
goto out_fail;
}
}
//為VolumeManager對象增加一個DirectVolume對象
vm->addVolume(dv);
}
......
}
......
return-1;
}
~~~
從上面代碼中發現,process_config的主要功能就是解析/etc/vold.fstab。這個文件的作用和Linux系統中的fstab文件很類似,就是設置一些存儲設備的掛載點,我的HTC G7手機上這個文件的內容如圖9-3所示:
:-: 
圖9-3 我的手機上vold.fstab內容
從上圖的紅框中可知:
- sdcard為volume的名字。
- /mnt/sdcard表示mount的位置。
- 1表示使用存儲卡上的第一個分區,auto表示沒有分區。現在有很多定制的ROM要求SD卡上存在多個分區。
- /devices/xxxx等內容表示MMC設備在sysfs中的位置。
根據G7的vold.fstab文件,可以構造一個DirectVolume對象。
注意,根據手機刷的ROM的不同,vold.fstab文件會有較大差異。
3. DirectVolume的分析
DirectVolume從Volume類派生,可把它看成是一個外部存儲卡(例如一張SD卡)在代碼中的代表物。它封裝了對外部存儲卡的操作,例如加載/卸載存儲卡、格式化存儲卡等。
下面是process_config函數中和DirectVolume相關的地方:
- 一個是創建DirectVolume。
- 另一個是調用DirectVolume的addpath函數。
它們的代碼如下所示:
**DirectVolume.cpp**
~~~
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<char *> 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中的路徑,G7的vold.fstab上有兩個路
//徑,見圖9-3中的最后一行。
int DirectVolume::addPath(const char *path) {
mPaths->push_back(strdup(path));
return0;
}
~~~
這里簡單介紹一下addPath的作用。addPath把和某個存儲卡接口相關的設備路徑與這個DirectVolume綁定到一起,并且這個設備路徑和Uevent中的DEVPATH是對應的,這樣就可以根據Uevent的DEVPATH找到是哪個存儲卡的DirectVolume發生了變動。當然手機上目前只有一個存儲卡接口,所以Vold也只有一個DirectVolume。
4. NM和VM交互
在分析NM模塊的數據處理時發現,NM模塊接收到Uevent事件后,會調用VM模塊進行處理,下面來看這塊的內容。
先回顧一下NM調用VM模塊的地方,代碼如下所示:
**NetlinkHandler.cpp**
~~~
void NetlinkHandler::onEvent(NetlinkEvent *evt){
VolumeManager *vm = VolumeManager::Instance();
constchar *subsys = evt->getSubsystem();
......
if (!strcmp(subsys, "block")) {
vm->handleBlockEvent(evt); //調用VM的handleBlockEvent
} elseif (!strcmp(subsys, "switch")) {
vm->handleSwitchEvent(evt);//調用VM的handleSwitchEvent
}
......
}
~~~
在上面代碼中,如果Uevent是block子系統,則調用handleBlockEvent;如果是switch,則調用handleSwitchEvent。handleSwitchEvent主要處理SD卡掛載磁盤的通知,比較簡單。這里只分析handleBlockEvent事件。
**VolumeManager.cpp**
~~~
void VolumeManager::handleBlockEvent(NetlinkEvent*evt) {
constchar *devpath = evt->findParam("DEVPATH");
/*
前面在process_config中構造的DirectVolume對象保存在了mVolumes中,它的定義如下:
typedef android::List<Volume *>VolumeCollection,也是一個列表。
注意它保存的是Volume指針,而我們的DirectVolume是從Volume派生的
*/
VolumeCollection::iterator it;
boolhit = false;
for (it = mVolumes->begin(); it !=mVolumes->end(); ++it) {
//調用每個Volume的handleBlockEvent事件,就我的G7手機而言,實際上將調用
//DirectVolume的handleBlockEvent函數。
if(!(*it)->handleBlockEvent(evt)) {
hit = true;
break;
}
}
}
~~~
NM收到Uevent消息后,DirectVolume也將應聲而動,它的handleBlockEvent的處理是:
**DirectVolume.cpp**
~~~
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;
}
~~~
關于DirectVolume針對不同Uevent的具體處理方式,后面將通過一個SD卡插入案例來分析。
5. VM模塊的總結
從前面的代碼分析中可知,VM模塊的主要功能是管理Android系統中的外部存儲設備。圖9-4描述了VM模塊的功能:
:-: 
圖9-4 VM模塊的職責
通過對上圖和前面代碼的分析可知:
- SD卡的變動(例如熱插拔)將導致Kernel發送Uevent消息給NM模塊。
- NM模塊調用VM模塊處理這些Uevent消息。
- VM模塊遍歷它所持有的Volume對象,Volume對象根據addPath添加的DEVPATH和Uevent消息中的DEVPATH來判斷,自己是否可以處理這個消息。
至于Volume到底如何處理Uevent消息,將通過一個實例來分析。
- 前言
- 第1章 閱讀前的準備工作
- 1.1 系統架構
- 1.1.1 Android系統架構
- 1.1.2 本書的架構
- 1.2 搭建開發環境
- 1.2.1 下載源碼
- 1.2.2 編譯源碼
- 1.3 工具介紹
- 1.3.1 Source Insight介紹
- 1.3.2 Busybox的使用
- 1.4 本章小結
- 第2章 深入理解JNI
- 2.1 JNI概述
- 2.2 學習JNI的實例:MediaScanner
- 2.3 Java層的MediaScanner分析
- 2.3.1 加載JNI庫
- 2.3.2 Java的native函數和總結
- 2.4 JNI層MediaScanner的分析
- 2.4.1 注冊JNI函數
- 2.4.2 數據類型轉換
- 2.4.3 JNIEnv介紹
- 2.4.4 通過JNIEnv操作jobject
- 2.4.5 jstring介紹
- 2.4.6 JNI類型簽名介紹
- 2.4.7 垃圾回收
- 2.4.8 JNI中的異常處理
- 2.5 本章小結
- 第3章 深入理解init
- 3.1 概述
- 3.2 init分析
- 3.2.1 解析配置文件
- 3.2.2 解析service
- 3.2.3 init控制service
- 3.2.4 屬性服務
- 3.3 本章小結
- 第4章 深入理解zygote
- 4.1 概述
- 4.2 zygote分析
- 4.2.1 AppRuntime分析
- 4.2.2 Welcome to Java World
- 4.2.3 關于zygote的總結
- 4.3 SystemServer分析
- 4.3.1 SystemServer的誕生
- 4.3.2 SystemServer的重要使命
- 4.3.3 關于 SystemServer的總結
- 4.4 zygote的分裂
- 4.4.1 ActivityManagerService發送請求
- 4.4.2 有求必應之響應請求
- 4.4.3 關于zygote分裂的總結
- 4.5 拓展思考
- 4.5.1 虛擬機heapsize的限制
- 4.5.2 開機速度優化
- 4.5.3 Watchdog分析
- 4.6 本章小結
- 第5章 深入理解常見類
- 5.1 概述
- 5.2 以“三板斧”揭秘RefBase、sp和wp
- 5.2.1 第一板斧--初識影子對象
- 5.2.2 第二板斧--由弱生強
- 5.2.3 第三板斧--破解生死魔咒
- 5.2.4 輕量級的引用計數控制類LightRefBase
- 5.2.5 題外話-三板斧的來歷
- 5.3 Thread類及常用同步類分析
- 5.3.1 一個變量引發的思考
- 5.3.2 常用同步類
- 5.4 Looper和Handler類分析
- 5.4.1 Looper類分析
- 5.4.2 Handler分析
- 5.4.3 Looper和Handler的同步關系
- 5.4.4 HandlerThread介紹
- 5.5 本章小結
- 第6章 深入理解Binder
- 6.1 概述
- 6.2 庖丁解MediaServer
- 6.2.1 MediaServer的入口函數
- 6.2.2 獨一無二的ProcessState
- 6.2.3 時空穿越魔術-defaultServiceManager
- 6.2.4 注冊MediaPlayerService
- 6.2.5 秋風掃落葉-StartThread Pool和join Thread Pool分析
- 6.2.6 你徹底明白了嗎
- 6.3 服務總管ServiceManager
- 6.3.1 ServiceManager的原理
- 6.3.2 服務的注冊
- 6.3.3 ServiceManager存在的意義
- 6.4 MediaPlayerService和它的Client
- 6.4.1 查詢ServiceManager
- 6.4.2 子承父業
- 6.5 拓展思考
- 6.5.1 Binder和線程的關系
- 6.5.2 有人情味的訃告
- 6.5.3 匿名Service
- 6.6 學以致用
- 6.6.1 純Native的Service
- 6.6.2 扶得起的“阿斗”(aidl)
- 6.7 本章小結
- 第7章 深入理解Audio系統
- 7.1 概述
- 7.2 AudioTrack的破解
- 7.2.1 用例介紹
- 7.2.2 AudioTrack(Java空間)分析
- 7.2.3 AudioTrack(Native空間)分析
- 7.2.4 關于AudioTrack的總結
- 7.3 AudioFlinger的破解
- 7.3.1 AudioFlinger的誕生
- 7.3.2 通過流程分析AudioFlinger
- 7.3.3 audio_track_cblk_t分析
- 7.3.4 關于AudioFlinger的總結
- 7.4 AudioPolicyService的破解
- 7.4.1 AudioPolicyService的創建
- 7.4.2 重回AudioTrack
- 7.4.3 聲音路由切換實例分析
- 7.4.4 關于AudioPolicy的總結
- 7.5 拓展思考
- 7.5.1 DuplicatingThread破解
- 7.5.2 題外話
- 7.6 本章小結
- 第8章 深入理解Surface系統
- 8.1 概述
- 8.2 一個Activity的顯示
- 8.2.1 Activity的創建
- 8.2.2 Activity的UI繪制
- 8.2.3 關于Activity的總結
- 8.3 初識Surface
- 8.3.1 和Surface有關的流程總結
- 8.3.2 Surface之乾坤大挪移
- 8.3.3 乾坤大挪移的JNI層分析
- 8.3.4 Surface和畫圖
- 8.3.5 初識Surface小結
- 8.4 深入分析Surface
- 8.4.1 與Surface相關的基礎知識介紹
- 8.4.2 SurfaceComposerClient分析
- 8.4.3 SurfaceControl分析
- 8.4.4 writeToParcel和Surface對象的創建
- 8.4.5 lockCanvas和unlockCanvasAndPost分析
- 8.4.6 GraphicBuffer介紹
- 8.4.7 深入分析Surface的總結
- 8.5 SurfaceFlinger分析
- 8.5.1 SurfaceFlinger的誕生
- 8.5.2 SF工作線程分析
- 8.5.3 Transaction分析
- 8.5.4 關于SurfaceFlinger的總結
- 8.6 拓展思考
- 8.6.1 Surface系統的CB對象分析
- 8.6.2 ViewRoot的你問我答
- 8.6.3 LayerBuffer分析
- 8.7 本章小結
- 第9章 深入理解Vold和Rild
- 9.1 概述
- 9.2 Vold的原理與機制分析
- 9.2.1 Netlink和Uevent介紹
- 9.2.2 初識Vold
- 9.2.3 NetlinkManager模塊分析
- 9.2.4 VolumeManager模塊分析
- 9.2.5 CommandListener模塊分析
- 9.2.6 Vold實例分析
- 9.2.7 關于Vold的總結
- 9.3 Rild的原理與機制分析
- 9.3.1 初識Rild
- 9.3.2 RIL_startEventLoop分析
- 9.3.3 RIL_Init分析
- 9.3.4 RIL_register分析
- 9.3.5 關于Rild main函數的總結
- 9.3.6 Rild實例分析
- 9.3.7 關于Rild的總結
- 9.4 拓展思考
- 9.4.1 嵌入式系統的存儲知識介紹
- 9.4.2 Rild和Phone的改進探討
- 9.5 本章小結
- 第10章 深入理解MediaScanner
- 10.1 概述
- 10.2 android.process.media分析
- 10.2.1 MSR模塊分析
- 10.2.2 MSS模塊分析
- 10.2.3 android.process.media媒體掃描工作的流程總結
- 10.3 MediaScanner分析
- 10.3.1 Java層分析
- 10.3.2 JNI層分析
- 10.3.3 PVMediaScanner分析
- 10.3.4 關于MediaScanner的總結
- 10.4 拓展思考
- 10.4.1 MediaScannerConnection介紹
- 10.4.2 我問你答
- 10.5 本章小結