先翻譯一段android的官方文檔,原文在:https://source.android.com/devices/storage/
android 6.0開始支援運作時權限管理的功能。運作時權限管量中當然也包括對read_external_storage和write_external_storage這兩個權限的動态管理。系統需要提供在不殺掉或重新開機已經運作的應用的情況下去動态授權的機制。目前系統是通過維護三個view來實作的:
/mnt/runtime/default: 針對對于存儲權限沒有特殊需求的情況。這也是adbd的其它系統元件使用的方式。
/mnt/runtime/read:對于申請read_external_storage權限的應用可見。
/mnt/runtime/write:對于申請write_external_storage權限的應用可見。
在zygote fork的時刻,我們為每個運作的應用建立一個命名空間,然後将其綁定到上面所進的三個view中作為初始的view。在運作時獲得新的授權後,vold會跳轉到這個裝載的命名空間并重新綁定新的view. 需要注意的一點是,如果權限降級,則一定會導緻應用被殺。
setns()方法從linux 3.8移植到了3.4就專為幹這事兒。有個permissionshosttest的cts測試用例用來保證這個功能的有效性。
在android 6.0,第三方應用沒有通路sdcard_r和sdcard_rw gid的權限。作為替代,通過上面所講的view的方式來控制。使用everybody gid跨使用者的互動會被阻止。
看了上面的介紹,我們來看代碼中是如何實作的。實作這個功能的函數在framework/base/core/jni/com_android_internal_os_zygote.cpp中的mountemulatedstorage函數。
第一步,先調用unshare系統調用去禁止同享命名空間。
unshare函數定義于sched.h中,用于将部分程序上下文資訊不共享父程序的,clone_newns是指定不共享命名空間。
下面是arm v8a aarch64下時調用unshare系統調用的代碼:
下面是第一步的代碼:
第二步,調用unmounttree函數:
首先解釋一下為什麼要做unmount,在init.rc裡面,root namespace已經預設地mount /mnt/runtime/default到/storage了,
請看init.rc的片段:
下面看代碼:
我們轉到unmounttree函數:
setmntent函數定義如下:
用于擷取系統mount的資訊。
具體去讀每一行的時候使用getmntent()函數。
結束時調用endmntent()函數,相當于fclose()。
getmntent讀取的是一個mntent結構體的結構,該結構定義于中:
下面代碼中,調用getmntent函數将目錄資訊讀出來放在一個清單中:
接着,通過調用umount2函數将這些目錄都unmount掉。
umount2函數原型如下,用于unmount檔案系統。
第三步,我們從unmounttree中回來,按照上面所講的幾種模式,分别設定不同路徑名:
第四步,根據第三步的模式值,重新mount。
第五步,針對多使用者的情況,額外需要做符号連結。
我們開始看這兩個參數,首先看mount_mode是從哪裡擷取的.
這往上一找,就是activitymanagerservice的startprocesslocked,我們摘錄個片斷看下:
然後我們看getexternalstoragemountmode,定義在frameworks/base/services/core/java/com/android/server/mountservice.java裡,周遊所有的policy,取其中最小的為最終結果。
下面我們再看pm中是如何為policy指派的,實作在frameworks/base/services/core/java/com/android/server/pm/packagemanagerservice.java中,通過checkuidpermission的結果來決定policy的結果: