Android多用戶原理和實現

Android 4.2中的一個比較顯著的改變就是加入了多用戶的支持。因多用戶手機專利早已被Symbian僱員註冊,故 android官方的多用戶切換目前僅支持平板設備。

多用戶模式的啓用

系統判斷當前設備是否支持多用戶模式的依據是配置文件config.xml中的config_multiuserMaximumUsers配置項。 其取值爲整型,決定着當前設備支持的最大用戶上限。默認值爲1,即不支持多用戶。如需啓用多用戶,則設置此值 爲大於1的值。在Nexus 7中,此值爲8

具體代碼的判斷位置在UserManager.java:

[java] view plain copy
  1. public static int getMaxSupportedUsers() {  
  2.         // Don't allow multiple users on certain builds  
  3.         if (android.os.Build.ID.startsWith("JVP")) return 1;  
  4.         return SystemProperties.getInt("fw.max_users",  
  5.                 Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers));  
  6.     }  


多用戶相關操作流程

對用戶的操作目前未對普通應用開放,其相關API都有hide註解,並需要system權限。此外,用戶的添加和移除還 要需android.Manifest.permission.MANAGE_USERS權限。

用戶添加流程

用戶添加是通過調用UserManager的public UserInfo createUser(String name, int flags)方法進行的。其具體實現在UserManagerService的同名方法中。

在調用時,系統進行如下操作:

  1. 檢查調用者是否具有所需權限。
  2. 對安裝和軟件包流程加鎖,保證線程安全
  3. 檢查多用戶環境是否到達用戶數量限制。如果沒有,創建用戶實例
  4. 爲新用戶創建相關目錄
  5. 序列化用戶列表
  6. 發送用戶建立廣播,MountService在收到此廣播後,調用createEmulatedVolumeForUserLocked方法爲用戶建 立相應的數據目錄

用戶的保存

用戶創建後,會首先在/data/system/users/userlist.xml文件中保存新增加用戶的id,創建/data/system/users/ 用戶id 目錄,並將用戶信息保存至其下的用戶id.xml 文件中。其內容包括一些基本的用戶信息。

用戶切換流程

用戶切換是通過調用ActivityManager的public boolean switchUser(int userId)方法進行。一般通過 ActivityManagerNative.getDefault().switchUser(int userId)進行調用。

在調用時,系統進行以下操作

  1. 檢查調用者是否具有所需權限。
  2. 獲取切換目標用戶信息,並設定當前用戶爲目標用戶
  3. WindowsManagerService設置當前用戶,鎖定屏幕
  1. 切換目標用戶狀態至啓動
  2. 廣播REPORT_USER_SWITCH_MSG和USER_SWITCH_TIMEOUT_MSG消息,設定用戶切換和切換超時時間(2秒),此超 時時間用於限定REPORT_USER_SWITCH_MSG廣播全程時間。
  3. 切換Activity堆棧至當前用戶
  4. 廣播ACTION_USER_SWITCHED消息,各接收組件進行相應操作。 大部分具體操作,比如存儲目錄切換、安全設置 切換等,都在此廣播後進行。

用戶移除流程

用戶移除是通過調用UserManager的 public boolean removeUser(int userHandle) 方法進行的。其具體實現同樣 是在UserManagerService的同名方法中。

在調用時,系統進行如下操作:

  1. 檢查調用者是否具有所需權限。
  2. 對軟件包變化加鎖
  3. 將用戶id加入待移除用戶列表,將用戶狀態設爲partial,這樣,在下次系統啓動時,會清除此用戶。
  4. 停止用戶,殺掉用戶相關進程。
  5. 發送用戶移除的廣播。廣播成功後,刪除用戶描述文件和數據文件。
  6. 序列化用戶列表

此外UserManager還提供了 public void wipeUser(int userHandle) 方法,用於刪除單個用戶的所有數據,但保留 用戶賬號。 此方法目前對應的底層實現尚未完成。

多用戶模式的API接口UserManager->UserManagerService

大致結構

與其它系統服務的實現類似,用戶管理也採用了經由Binder調用的遠程服務機制。UserManager爲暴露給用戶的接 口,UserManagerService爲接口的底層實現。其類圖如下所示:

android.os.UserManager

UserManager是暴露出來的應用程序接口。對於普通應用程序,提供用戶數查詢,用戶狀態判斷和用戶序列號查詢 等基本功能。 普通應用沒有用戶操作權限

對於系統應用,UserManager提供了創建/刪除/擦除用戶、用戶信息獲取、用戶句柄獲取等用戶操作的接口。均由遠 程調用UserManagerService服務的對應方法實現。

isUserAGoat()

UserManager中提供了一個名爲isUserAGoat()的方法。源碼中此方法直接返回了false。此方法的加入純粹是爲了給 枯燥的編程生活帶來一絲樂趣,以便寫出:

[java] view plain copy
  1. while(!isUserAGoat()){  
  2.    
  3. }  


這樣的語句。

com.android.server.pm.UserManagerService

與其它大部分Service一樣,UserManagerService的實現採用了 單例模式。在服務中,由組成爲UserInfo類的散列 表mUsers維護所有的用戶狀態。

mUsers在系統啓動時由/data/system/users/userlist.xml讀取生成,並在運行期間動態改變。所有用戶的添加、刪 除操作,都最終序列化回此文件中。

com.android.server.am.ActivityManagerService

ActivityManagerService目前加入了多用戶支持。負責維護設備中存在的所有用戶狀態。服務以下述變量來記錄當 前處於“啓動”狀態的用戶。

[java] view plain copy
  1. /** 
  2.      * Which uses have been started, so are allowed to run code. 
  3.      */  
  4.     final SparseArray mStartedUsers = new SparseArray();  
  5.    
  6.     /** 
  7.      * LRU list of history of current users.  Most recently current is at the end. 
  8.      */  
  9.     final ArrayList mUserLru = new ArrayList();  
  10.    
  11.     /** 
  12.      * Constant array of the users that are currently started. 
  13.      */  
  14.     int[] mStartedUserArray = new int[] { 0 };  


用戶的啓動狀態對象爲com.android.server.am.UserStartedState。其中指定的用戶狀態有四種:

  • public final static int STATE_BOOTING = 0; //用戶啓動
  • public final static int STATE_RUNNING = 1; //運行中
  • public final static int STATE_STOPPING = 2; //停止中
  • public final static int STATE_SHUTDOWN = 3; //用戶關閉狀態

完整的用戶生命週期爲:
BOOTING->RUNNING->STOPPING->SHUTDOWN

用戶必須處於RUNNING狀態時,才能作爲切換的目標用戶。所以在用戶切換流程中,首先要判斷當前用戶的狀態, 並啓動STOPPING/SHUTDOWN狀態的用戶。

多用戶模式的牽涉面

鎖屏界面

用戶最先體驗到多用戶的入口位置即爲鎖屏界面。鎖屏界面中加入了用戶切換組件: KeyguardMultiUserSelectorView類。

該類在設備允許多用戶存在的情況下,顯示當前所有用戶的列表。並在用戶進行選擇後,調用 ActivityManagerNative.getDefault().switchUser(int userId)方法進行用戶切換。

外部存儲

對於每個用戶,Android都爲其分配了單獨的存儲空間。標準的支持多用戶的外部存儲空間是由init.rc定義的環境 變量所指定:

[java] view plain copy
  1. # See storage config details at <a href="http://source.android.com/tech/storage/">http://source.android.com/tech/storage/</a>  
  2. mkdir /mnt/shell/emulated 0700 shell shell  
  3. mkdir /storage/emulated 0555 root root  
  4.    
  5. export EXTERNAL_STORAGE /storage/emulated/legacy  
  6. export EMULATED_STORAGE_SOURCE /mnt/shell/emulated  
  7. export EMULATED_STORAGE_TARGET /storage/emulated  
  8.    
  9. # Support legacy paths  
  10. symlink /storage/emulated/legacy /sdcard  
  11. symlink /storage/emulated/legacy /mnt/sdcard  
  12. symlink /storage/emulated/legacy /storage/sdcard0  
  13. symlink /mnt/shell/emulated/0 /storage/emulated/legacy  


在Dalvik虛擬機初始化的過程中,會以dalvik_system_Zygote.cpp中的mountEmulatedStorage函數,使用帶有 MS_BIND參數的mount命令, 將用戶對應的外部存儲卡目錄mount到上述定義的TARGET目錄下。其判斷應用userid的 方式爲: 以當前應用的uid/100000,獲得對應的userid,這段邏輯位於system/core/libcutils/multiuser.c中。

而Environment類中相應的獲取外部存儲目錄的方法,也是由上述環境變量所獲得。對於每個用戶,其標準外部存 儲路徑爲:

EMULATED_STORAGE_TARGET/userid/

比如:

/storage/emulated/0 爲主用戶的外部存儲路徑。

包管理(PackageManagerService)

在多用戶環境下,所有用戶安裝的應用仍然同以前一樣,放置於/data/app目錄下。但原先/data/data的數據存儲位 置目前僅對主用戶有效,其餘用戶的數據存儲目錄則位於/data/user/用戶id/目錄下。 此目錄的創建是在創建用戶 時由前述的MountService完成的。

對於每個用戶,系統都會以PackageuserState類來維護其安裝的軟件狀態。此列表以散列表的形式存在,由 PackageSettingBase類維護。所有的包——用戶關係和狀態最終仍然序列化至/data/system/package.xml中,並保留 /data/system/package-backup.xml作爲備份。

可能的發展

Guest用戶的實現

目前代碼中已經存在諸如 isGuestEnabled() 之類的方法。但沒有對開發公開,可以預計今後會加入Guest用戶, 實現“隨手玩玩”模式。


from: http://blog.csdn.net/jcgu/article/details/12615661

發佈了42 篇原創文章 · 獲贊 44 · 訪問量 85萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章