1Vold功能分析
1.1Vold的主要作用:
Android系統中為了統一管理磁盤而引入Vold 負責磁盤的掛載等。
vold是一個中間層,負責來連接上層以及驅動層。其實vold主要抓住兩點:一是在CommandListener.cpp中處理上層發下來的命令,二是在NetlinkHandler.cpp中處理底層發送上來的各種信息。
vold與上層之間是通過DomainSocket來通訊的,與下層之間是通過NetLinkSocket和sysfs來實現通訊的,即通過NetLinkSocket來截取usb驅動和mmc發送上來的uevent事件。主要處理subsys= block的uevent,即block設備的添加、移除等。
有關磁盤管理部分其他文章分析。
1.2Vold啟動
Vold是在init進程中啟動的,在init進程中將解析 init.rc 文件,在該文件中有啟動Vold的配置。在這里將啟動 Vold ,并且創建一個 socket。這socket 主要是為了與 framework 層通信。 如下面所示:
servicevold/system/bin/vold
class core
socket vold stream 0660 root mount
ioprio be 2
1.3Vold通信
由于它負責兩方面的通信,所以他有一共有兩條socket:
1)Domainsocket: 負責vold與framework層的信息傳遞;
Domainsocket是在init進程啟動Vold時創建的,這是一個用于和framework層通信的 socket,在android系統中有個封裝類Localsocket。
在framework層的MountService類中調用下面
mNativeConnector = new?NativeDaemonConnector(new NativeCallbackReceiver(), "vold", 10,VOLD_TAG,25);
MountService通過NativeDaemonConnector和Vold中的CommandListener建立socket通信。其中 mSocket值為“vold”表示要獲得 socket的名字,該 socket如上所述,是在vold被 init的時候創建的,并且保存為全局變量。
NavtiveDaemonConnector位于frameworks\base\services\java\com\android\server目錄下,他會開啟一個線程不停的監聽來自vold的消息,所以這里有可能消息堵塞。NavtiveDaemonConnector ??類封裝實例化一個 LocalSocket 來與 vold 通信。LocalSocket 里面有一個類 LocalSocketImpl ?,該類部分函數時通過 JNI實現的。
2)Netlinksocket: 負責接受kernel的信息;
Netlinksocket是在NetLinkManager的start()中創建的。
1.4事件處理
這里通過對兩個連結的監聽,完成對動態事件的處理,以及對上層應用操作的響應。我們來具體分析一下代碼過程。
1)kernel發出 uevent
NetLinkManager 檢測 kernel 發出的 uevent,經過解析后,調用NetLinkHandler::onEvent()。如下所示:
void?NetlinkHandler::onEvent(NetlinkEvent *evt) {
? ?VolumeManager *vm = VolumeManager::Instance();
? ?constchar*subsys = evt->getSubsystem();
? ?if(!subsys) {
? ? ? ? ? ?SLOGW("No subsystem found innetlinkevent");
? ? ? ? ? ?return;
? ?}
if(!strcmp(subsys,"block")) {
? ? ?vm->handleBlockEvent(evt);
}elseif(!strcmp(subsys,"usb")|| !strcmp(subsys,"scsi_device")) {
? ? ?SLOGW("subsystem found innetlinkevent");
? ? MiscManager *mm = MiscManager::Instance();?
? ?mm->handleEvent(evt);
}
}
對于不同的時間類型分別處理。“block”事件由 VolumeManager的 handleBlockEvent(evt)處理,最終調用的是DirectVolume的 handleBlockEvent(evt)。block主要指對 volume的一些操作的事件,如插入,拔掉外接存儲設備。
vm->handleBlockEvent(evt)->DirectVolume::handleBlockEvent(NetlinkEvent *evt).
DirectVolume::handleBlockEvent這個方法就是具體負責拔插操作的方法。
“scsi_device"”事件由MiscManager處理。現在一般是處理3G DONGLE熱拔插設備。
2)framework層發出命令
和上面過程正好相反,CommandListener檢測到framework層的命令,調用VolumeManager的函數,VolumeManager遍歷Volume清單找出對應的volume, 調用volume的函數。 而volume類中的相關調用,最終調用到Linux庫函數,完成相關操作。
代碼位于CommandListener.cpp中:
if(!strcmp(argv[1],"list")) {
? ?returnvm->listVolumes(cli);
}elseif(!strcmp(argv[1],"mount")) {
? ?if(argc != 3) {
? ? ? ? ?cli->sendMsg(ResponseCode::CommandSyntaxError,"Usage: volume mount ",false);
? ? ? ?return?0;
? ? }
? ? rc = vm->mountVolume(argv[2]);
}elseif(!strcmp(argv[1],"unmount"))
......
1.5Uevent簡介
Uevent 由內核發出,通過netlink sokect 來傳遞給 vold,在 kobject 被創建的時候,就會發生uevent的傳遞。 對于未傳遞的uevent, 會在kset下產生uevent文件, 這是供用戶態觸發uevent使用的,通過向uevent 檔寫入 action(add,remove 等) ,可以觸發一個 uevent,這些 uevent
可以被vold 捕獲,從而完成未完成的 vold 處理。在系統啟動的時候,vold 未啟動的時候,這些 uevent 寫入了 uevent 檔,vold 啟動后,會掃描 sys 目錄查找 uevent,然后觸發它們,來完成之前未完成的事宜。uevent文件的內容,就是uevent 事件的數據。
1.6APP2SD
1.6.1APP2SD簡介
App2SD的功能是將apk安裝在外部存儲器上,而不是內部data分區,這樣可以節省內部data分區。從Gingerbread之后就開始支持這個功能,具體的方法是在apk的AndroidManifest.xml中聲明要安裝的位置。
android:installLocation="preferExternal" ???... >
1.6.2APP2SD實現簡單分析
app2sd的實現,需要vold里面asec相關操作支持才能完成,否則安裝到外部存儲器的apk在機器重啟后就會消失。
PM在判斷akp是安裝在內部還是外部之后,會調用createAsec(...bool isExternal)來創建用來存放apk數據的一個Fat或者Ext4的文件系統。
具體調用流程如下:PackageHelper::createSdDir()--->mountService.createSecureContainer(cid, sizeMb, "fat", sdEncKey, uid, isExternal);---CommandListener-->VolumeManager::createAsec(const char *id, ...bool isExternal)
其中創建asec的流程請詳細分析代碼,其中主要思路是在/mnt/secure/asec/id.asec這路徑下創建一個loop設備,然后把這個loop設備掛載到/mnt/asec/目錄下。此時如果vold沒有創建/mnt/secure/asec的話,這個目錄就是在根文件系統中的一個臨時文件,在重啟后就會消失。具體看代碼VolumeManager.cpp中有關asec和obb部分的實現。
1.6.3/mnt/secure/asec/的由來
在Volume.cpp中的mountVol()函數中,如果是flash掛載就按照如下流程處理:
1).把flash設備掛載在/mnt/secure/staging
2).創建/mnt/secure/staging/.android_secure文件夾
3).Bind mount /mnt/secure/staging/.android_secure -> /mnt/secure/asec //一個文件系統
4).Mount a read-only, zero-sized tmpfs ?on /android_secure
5).把/mnt/secure/staging move到mountpoint
這樣子看來其實/mnt/secure/asec/是flash中的一個文件夾.android_secure被掛載成了一個fat的文件系統,這樣就保證在里面創建的文件不會丟失。
掛載成功后在mount命令中可以看到如下信息:
/dev/block/vold/31:9 /mnt/sdcard vfat rw,dirsync,nosuid,nodev,noexec........
/dev/block/vold/31:9 /mnt/secure/asec vfat rw,dirsync,nosuid,nodev............
1.6.4判斷是否支持APP2SD
Android上層代碼中判斷是否支持APP2SD的方法一般是判斷主分區是否是虛擬的,如果不是虛擬的就要實現APP2SD。
public static boolean isExternalStorageEmulated() {
? ?final StorageVolume primary = getPrimaryVolume();
? ?return (primary != null && primary.isEmulated());
}
2MountService簡介
2.1MountService功能介紹:
MountService是一個服務類,在ServiceManager中注冊為系統服務,提供對外部存儲設備的管理、查詢等服務,并在存儲設備狀態變更時發出通知。MountService起到了一個承上啟下的作用,向上公開方法供上層對存儲設備進行操作(enable/disable/mount…),并在存儲設備狀態變更時發出通知。向下接收Vold發來的事件(設備狀態變更,設備插入,設備移除等),同時也會將命令發送給Vold,進行更底層的操作。
MountService中的主要方法如下表所示:
2.2MountService同Vold的交互
MountService通過NativeDaemonConnector來和Vold傳遞信息, NativeDaemonConnector是一個繼承自 Runnable 的類,它的構造函數中需要傳入一個回調接口,在它的 run 函數里會創建 socket 并一直監聽,當收到需要上層處理的信息時,會調用傳入接口中的 onEvent。NativeDaemonConnector中也提供了 doCommond方法向 socket 另一端發送信息。 MountService 會在構造函數中創建一個 NativeDaemonConnector的實例,把自身作為參數傳遞進去用以回調,然后開啟這個線程,這樣就創建了同 Vold 通信的 socket 并監聽來自vold的消息。 這些消息包括: VolumeStateChange, ShareAvailabilityChange, VolumeDiskInserted,VolumeDiskRemoved,VolumeBadRemoval,收到消息后回調 onEvent 做處理。MountService也能通過調用 doCommond向 Vold發送消息。一個簡單的發送命令的代碼:
public?int?mountSecureContainer(String id, String key,intownerUid) {
? int?rc = StorageResultCode.OperationSucceeded;
? try{
? ? ? ? mConnector.execute("asec","mount", id,newSensitiveArg(key),ownerUid);
? }catch(NativeDaemonConnectorException e) {
? ? ?int?code = e.getCode();
? ? ?if(code != VoldResponseCode.OpFailedStorageBusy) {
? ? ? ? ? ?rc = StorageResultCode.OperationFailedInternalError;
? ? ? }
? }
returnrc;
}
2.3MountService同StroageManager的交互
MountService 是運行在 SystemService 這個進程中,所以上層應用無法直接訪問,StorageManager就是提供給應用層來訪問存儲服務的,它通過Binder 機制與 MountService所在進程進行通信,將使用者的請求轉發進MountService 中進行處理。 目前StorageManager中支持的方法有enableUsbMassStorage, disableUsbMassStorage,isUsbMassStorageConnected,isUsbMassStorageConnected。在 StorageManager 的構造函數中,還通過調用 MountService中的 registerListener 函數來注冊 listener 到 MountService,同時,它自己也提供了registerListener 函數供其它應用來注冊 listener,這樣,當 MountService 知道存儲設備狀態變更時,會調用 StorageManager中 listener的方法,而 StorageManager又會繼續回調上去上層應用也就可以做相應的操作。
2.4MountService同PackageManagerService的交互
PackageManagerService 是一個用于管理設備中所有 apk的服務。 當外部存儲設備被掛載或卸除時,會通知 PackagemanagerService 更新狀態。然后 PackageManagerService 通過PackageHelper調用MountService中 secure container相關方法。比如app 安裝在外部存儲上,當外部存儲狀態變化就會通知應用更新。