Android中的多用戶與Windows的多用戶類似,可以支持多個用戶使用系統。通常,第一個在系統中注冊的用戶將默認成為系統管理員。
不同用戶的設置各不相同,并且不同用戶安裝的應用及應用數據也不相同。但是系統中和硬件相關的設置則是共用的,例如網絡設置等。
Android與Windows不同,用戶切換后前面用戶運行的后臺進程還可以繼續運行。這樣,進行用戶切換時無須中斷一些后臺進行的耗時操作(如下載)。
UserManagerService的主要功能是創建和刪除用戶,以及查詢用戶信息。
UserManagerService是在PackageManagerService的構造方法中創建的,如下:
[java] view plain copy sUserManager = new UserManagerService(context, this, mInstallLock, mPackages); UserManagerService的構造方法,如下:[java] view plain copy UserManagerService(Context context, PackageManagerService pm, Object installLock, Object packagesLock) { this(context, pm, installLock, packagesLock, Environment.getDataDirectory(), new File(Environment.getDataDirectory(), "user")); } 調用另一個構造方法,并多傳遞了兩個參數:/data目錄和/data/user文件。如下:[java] view plain copy PRivate UserManagerService(Context context, PackageManagerService pm, Object installLock, Object packagesLock, File dataDir, File baseUserPath) { mContext = context; mPm = pm; mInstallLock = installLock; mPackagesLock = packagesLock; mHandler = new Handler(); synchronized (mInstallLock) { synchronized (mPackagesLock) { mUsersDir = new File(dataDir, USER_INFO_DIR);// /data/system/users mUsersDir.mkdirs(); // Make zeroth user directory, for services to migrate their files to that location File userZeroDir = new File(mUsersDir, "0");//第一個用戶的目錄/data/system/users/0 userZeroDir.mkdirs(); mBaseUserPath = baseUserPath;// /data/user FileUtils.setPermissions(mUsersDir.toString(), FileUtils.S_IRWXU|FileUtils.S_IRWXG |FileUtils.S_IROTH|FileUtils.S_IXOTH, -1, -1); mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);// /data/system/users/userlist.xml initDefaultGuestRestrictions(); readUserListLocked();//分析用戶的信息文件 // Prune out any partially created/partially removed users. ArrayList<UserInfo> partials = new ArrayList<UserInfo>(); for (int i = 0; i < mUsers.size(); i++) { UserInfo ui = mUsers.valueAt(i); if ((ui.partial || ui.guestToRemove) && i != 0) { partials.add(ui);//尋找userlist.xml文件中屬性partial為true的用戶 } } for (int i = 0; i < partials.size(); i++) { UserInfo ui = partials.get(i); Slog.w(LOG_TAG, "Removing partially created user #" + i + " (name=" + ui.name + ")"); removeUserStateLocked(ui.id);//屬性為partial表示創建沒完成的用戶,從系統中移除 } sInstance = this; } } } 在構造方法中創建了幾個目錄:/data/system/users、data/system/users/0、/data/system/users/userlist.xml等。之后設置基本的限制條件,如下:[java] view plain copy private void initDefaultGuestRestrictions() { if (mGuestRestrictions.isEmpty()) { mGuestRestrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true);//"no_outgoing_calls" mGuestRestrictions.putBoolean(UserManager.DISALLOW_SMS, true);//"no_sms" } } 然后調用readUserListLocked()方法解析/data/system/users/userlist.xml文件,這個文件保存了系統中所有用戶的id信息。文件內容如下:[java] view plain copy <?xml version='1.0' encoding='utf-8' standalone='yes' ?> <users nextSerialNumber="11" version="5"> <guestRestrictions> <restrictions no_outgoing_calls="true" no_sms="true" /> </guestRestrictions> <user id="0" /> <user id="10" /> </users> 得到id信息后還有讀取保存了用戶注冊信息的xml文件,以用戶id的數字表示,如0.xml。內容如下:[java] view plain copy <?xml version='1.0' encoding='utf-8' standalone='yes' ?> <user id="0" serialNumber="0" flags="19" created="0" lastLoggedIn="1436392413074"> <name>Owner</name> <restrictions /> </user> 讀取用戶的xml文件內容后,將根據文件內容來創建和初始化一個UserInfo對象來保存信息,并把該對象加入到mUsers列表中。如果xml文件中某個用戶的屬性中有partial="true',則表示這個用戶沒有完全創建成功,需要從系統中移除掉。這樣UserManagerService的初始化工作就完成了,主要的工作是分析userlist.xml文件,并創建了mUsers列表中的UserInfo對象。
UserManagerService服務中添加用戶的接口是createUser(),如下:
[java] view plain copy public UserInfo createUser(String name, int flags) { checkManageUsersPermission("Only the system can create users"); return createUserInternal(name, flags, UserHandle.USER_NULL); } createUser()方法首先檢查調用者的權限,只有system身份的進程才能調用這個方法。然后調用createUserInternal()方法繼續執行,如下:[java] view plain copy private UserInfo createUserInternal(String name, int flags, int parentId) { if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean( UserManager.DISALLOW_ADD_USER, false)) {//獲取是否有添加用戶的權限 Log.w(LOG_TAG, "Cannot add user. DISALLOW_ADD_USER is enabled."); return null; } final boolean isGuest = (flags & UserInfo.FLAG_GUEST) != 0; final long ident = Binder.clearCallingIdentity(); UserInfo userInfo = null; try { synchronized (mInstallLock) { synchronized (mPackagesLock) { UserInfo parent = null; if (parentId != UserHandle.USER_NULL) { parent = getUserInfoLocked(parentId); if (parent == null) return null; } // 如果添加的不是Guest用戶,而且達到上限數,返回 if (!isGuest && isUserLimitReachedLocked()) { return null; } // 如果添加的是Guest用戶,但是已經有Guest用戶存在,返回 if (isGuest && findCurrentGuestUserLocked() != null) { return null; } // if ((flags & UserInfo.FLAG_MANAGED_PROFILE) != 0 && numberOfUsersOfTypeLocked(UserInfo.FLAG_MANAGED_PROFILE, true) >= MAX_MANAGED_PROFILES) {//目前MAX_MANAGED_PROFILES最大是1 return null;//如果創建的是一個用戶的profile,檢查該用戶是否已經有profile } int userId = getNextAvailableIdLocked();//得到新用戶ID userInfo = new UserInfo(userId, name, null, flags); File userPath = new File(mBaseUserPath, Integer.toString(userId)); userInfo.serialNumber = mNextSerialNumber++; long now = System.currentTimeMillis(); userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0; userInfo.partial = true;//將partial屬性設為true Environment.getUserSystemDirectory(userInfo.id).mkdirs(); mUsers.put(userId, userInfo);//把新用戶信息添加到mUsers writeUserListLocked();//把用戶信息寫到文件userlist.xml中 if (parent != null) { if (parent.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) { parent.profileGroupId = parent.id; writeUserLocked(parent); } userInfo.profileGroupId = parent.profileGroupId; } writeUserLocked(userInfo);//創建用戶的信息文件10.xml mPm.createNewUserLILPw(userId, userPath); userInfo.partial = false;//創建成功,將partial設為false writeUserLocked(userInfo);//更新用戶的信息文件 updateUserIdsLocked(); Bundle restrictions = new Bundle(); mUserRestrictions.append(userId, restrictions); } } if (userInfo != null) {//廣播用戶創建成功的消息 Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id); mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL, android.Manifest.permission.MANAGE_USERS); } } finally { Binder.restoreCallingIdentity(ident); } return userInfo; (1)檢查調用進程所屬用戶是否被限制添加用戶。(2)調用isUserLimitReachedLocked()方法來確定是否還能創建更多的用戶。isUserLimitReachedLocked()方法將調用getMaxSupportedUsers()方法得到系統允許的最大用戶數和當前用戶數進行比較。
(3)調用getNextAvailableIdLocked()方法得到新的用戶id。getNextAvailableIdLocked()方法會從10開始查找,如果某個用戶Id還沒有使用,則將它返回。
(4)根據用戶id創建用戶的數據目錄。同時為用戶創建UserInfo對象,并加入到mUsers列表中。
(5)向userlist.xml文件中加入用戶的id信息。在/data/system/users/目錄下創建以用戶id數字表示的xml文件,保存UserInfo中的用戶信息,文件中partial屬性會被設為true。
(6)調用PackageManagerService的createNewuserLILPw()方法,在新建用戶的目錄下為所有應用創建數據目錄。
(7)更新用戶的信息文件,將xml文件中的partial屬性移除,這樣新用戶就創建完成了。使用partial屬性的目的是為了防止調用createNewUserLILPw()方法出錯,不能成功創建用戶。利用這個標志,系統重啟時可以清除掉創建失敗的用戶。
(8)廣播加入新用戶的消息Intent.ACTION_USER_ADDED。
UserManagerService服務中刪除用戶的接口是removeUser()方法,如下:
[java] view plain copy public boolean removeUser(int userHandle) { checkManageUsersPermission("Only the system can remove users"); if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean( UserManager.DISALLOW_REMOVE_USER, false)) { Log.w(LOG_TAG, "Cannot remove user. DISALLOW_REMOVE_USER is enabled."); return false; }//檢查調用進程的權限以及進程所屬的用戶是否被“限制” long ident = Binder.clearCallingIdentity(); try { final UserInfo user; synchronized (mPackagesLock) { user = mUsers.get(userHandle); if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) { return false; } // 將userHandle放入mRemovingUserIds列表,防止重復刪除一個用戶 mRemovingUserIds.put(userHandle, true); try { mAppOpsService.removeUser(userHandle); } catch (RemoteException e) { Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e); } // Set this to a partially created user, so that the user will be purged // on next startup, in case the runtime stops now before stopping and // removing the user completely. user.partial = true; // Mark it as disabled, so that it isn't returned any more when // profiles are queried. user.flags |= UserInfo.FLAG_DISABLED; writeUserLocked(user);//更新用戶信息文件 } if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID && user.isManagedProfile()) { // Send broadcast to notify system that the user removed was a // managed user. sendProfileRemovedBroadcast(user.profileGroupId, user.id); } if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle); int res; try {//停止正在運行的用戶 res = ActivityManagerNative.getDefault().stopUser(userHandle, new IStopUserCallback.Stub() {//回調函數 @Override public void userStopped(int userId) { finishRemoveUser(userId); } @Override public void userStopAborted(int userId) { } }); } catch (RemoteException e) { return false; } return res == ActivityManager.USER_OP_SUCCESS; } finally { Binder.restoreCallingIdentity(ident); } } removeUser()方法并沒有立即刪除用戶相關的文件,而是首先將用戶信息中的partial屬性改為true。這樣的好處是,如果后面的過程被意外終止了,系統重啟后還是會刪除該用戶所有目錄和文件。考慮到被刪除的用戶可能正在運行,因此,removeUser()方法要調用ActivityManagerService的stopUser()方法來改變該用戶的運行狀態,結束后,AMS將調用函數參數中的回調方法,最終調用到finishRemoveUser()方法。如下:
[java] view plain copy void finishRemoveUser(final int userHandle) { if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userHandle); // Let other services shutdown any activity and clean up their state before completely // wiping the user's system directory and removing from the user list long ident = Binder.clearCallingIdentity(); try { Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle); mContext.sendOrderedBroadcastAsUser(addedIntent, UserHandle.ALL, android.Manifest.permission.MANAGE_USERS, new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (DBG) { Slog.i(LOG_TAG, "USER_REMOVED broadcast sent, cleaning up user data " + userHandle); } new Thread() { public void run() { synchronized (mInstallLock) { synchronized (mPackagesLock) { removeUserStateLocked(userHandle); } } } }.start(); } }, null, Activity.RESULT_OK, null, null); } finally { Binder.restoreCallingIdentity(ident); } } 在finishRemoveUser()方法中發出了廣播Intent.ACTION_USER_REMOVED,同時也創建了一個BroadcastReceiver對象來接收這個廣播。注意,這時創建的廣播接收器將最后才能收到廣播通知。這是為了讓系統中其他關心這條廣播的地方先處理,最后才回到這里繼續完成刪除用戶的工作。收到廣播后,考慮到后面的工作還很耗時,因此,在onReceiver()方法中啟動了一個線程來運行removeUserStateLocked()方法。代碼如下:[java] view plain copy private void removeUserStateLocked(final int userHandle) { // Cleanup package manager settings調用PackageManagerService的cleanUpUserLILPw方法刪除所有用戶目錄下app的數據 mPm.cleanUpUserLILPw(this, userHandle); // Remove this user from the list在mUsers列表中刪除用戶的UserInfo對象 mUsers.remove(userHandle); mRestrictionsPinStates.remove(userHandle);//從mRestrictionsPinStates列表中移除用戶 // Remove user file刪除用戶的信息文件 AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX)); userFile.delete(); // Update the user list writeUserListLocked();//更新文件userlists.xml,移除用戶信息 updateUserIdsLocked(); removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle));//刪除用戶目錄下的所有文件 } removeUserStateLocked()方法將刪除所有用戶相關的文件。首先是app的數據文件,然后是用戶信息文件和userlist.xml中的用戶Id信息,最后將用戶目錄下的所有文件都刪除。在多用戶的環境中,所有用戶安裝的應用還是位于/data/app目錄,但是這些應用的數據在每個用戶的目錄/data/user/<用戶id>/下都有單獨的一份。目錄/data/data也還保存應用的數據,但這些數據只對id為0的用戶有效。
上面講到創建用戶時會調用PackageManagerService的createNewUserLILPw()方法,如下:
[java] view plain copy /** Called by UserManagerService */ void createNewUserLILPw(int userHandle, File path) { if (mInstaller != null) { mInstaller.createUserConfig(userHandle); mSettings.createNewUserLILPw(this, mInstaller, userHandle, path); } } createNewUserLILPw()方法只是調用了mInstaller的createUserConfig()和mSettings的createNewUserLILPw()方法。我們先看下mInstaller的createUserConfig()方法是如何處理的,如下:
[java] view plain copy public int createUserConfig(int userId) { StringBuilder builder = new StringBuilder("mkuserconfig"); builder.append(' '); builder.append(userId); return mInstaller.execute(builder.toString()); } mInstaller是InstallerConnection的對象,這里調用InstallConnection對象的execute()方法,如下:[java] view plain copy public int execute(String cmd) { String res = transact(cmd); try { return Integer.parseInt(res); } catch (NumberFormatException ex) { return -1; } } 這里實現了與installd服務通信的過程,具體操作是由installd服務完成的。再看下mSettings的createNewUserLILPw()方法,我們知道mSettings中保存的是從文件中讀取的所有應用的信息。如下:[java] view plain copy void createNewUserLILPw(PackageManagerService service, Installer installer, int userHandle, File path) { path.mkdir();//創建用戶保存應用數據的目錄/data/user FileUtils.setPermissions(path.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH, -1, -1); for (PackageSetting ps : mPackages.values()) { if (ps.pkg == null || ps.pkg.applicationInfo == null) { continue; }//對每一個應用在用戶的目錄下創建數據目錄 // Only system apps are initially installed. ps.setInstalled((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0, userHandle); // Need to create a data directory for all apps under this user. installer.createUserData(ps.name, UserHandle.getUid(userHandle, ps.appId), userHandle, ps.pkg.applicationInfo.seinfo); } readDefaultPreferredAppsLPw(service, userHandle); writePackageRestrictionsLPr(userHandle); } createNewUserLILPw()方法會遍歷mPackages列表,這個列表保存的是系統中所有已安裝的應用信息,然后針對每一個應用調用installer的createUserData()方法,調用Installer的方法最終會調用installd守護進程的命令,這里最終調用installd的“mkuserdata”命令來執行應用數據目錄的創建工作。[java] view plain copy public int createUserData(String name, int uid, int userId, String seinfo) { StringBuilder builder = new StringBuilder("mkuserdata"); builder.append(' '); builder.append(name); builder.append(' '); builder.append(uid); builder.append(' '); builder.append(userId); builder.append(' '); builder.append(seinfo != null ? seinfo : "!"); return mInstaller.execute(builder.toString()); } 同時每個應用的設置對象PackageSetting中都有一個數組userState,表示應用已經被安裝到哪些用戶中,這里調用ps的setInstalled方法就是向數組userState中插入用戶的信息。調用readDefaultPreferredAppsLPw()方法是為了分析目錄/etc/preferred-apps所有的xml文件,這個目錄下的xml文件保存的是設備使用者指定的響應某個intent的最合適的組件。因為每個用戶都可以指定它喜歡的最合適的組件,所以每個用戶都需要在mPreferredActivities列表中加入它自己的PreferredIntentResolver對象,這個對象中保存的就是intent和組件的關聯數據。
PackageManagerService中刪除用戶的方法是cleanUpUserLILPw()方法,如下:
[java] view plain copy /** Called by UserManagerService */ void cleanUpUserLILPw(UserManagerService userManager, int userHandle) { mDirtyUsers.remove(userHandle); mSettings.removeUserLPw(userHandle); mPendingBroadcasts.remove(userHandle); if (mInstaller != null) { // Technically, we shouldn't be doing this with the package lock // held. However, this is very rare, and there is already so much // other disk I/O going on, that we'll let it slide for now. mInstaller.removeUserDataDirs(userHandle);//刪除用戶目錄下所有應用的數據 } mUserNeedsBadging.delete(userHandle); removeUnusedPackagesLILPw(userManager, userHandle); } cleanUpUserLILPw方法主要的工作就是調用mInstaller的removeUserDataDirs()方法來刪除用戶目錄下所有應用的數據。同時調用mSettings的removeUserLPw()方法來刪除PackageManagerService中和用戶相關的數據,如下:[java] view plain copy void removeUserLPw(int userId) { Set<Entry<String, PackageSetting>> entries = mPackages.entrySet(); for (Entry<String, PackageSetting> entry : entries) { entry.getValue().removeUser(userId); }<span style="font-family: Arial, Helvetica, sans-serif;">//刪除每個應用信息中該用戶的信息</span> mPreferredActivities.remove(userId);//刪除mPreferredActivities列表中該用戶的數據 File file = getUserPackagesStateFile(userId); file.delete();//刪除該用戶目錄下的package-restrictions.xml文件 file = getUserPackagesStateBackupFile(userId); file.delete(); removeCrossProfileIntentFiltersLPw(userId); }UserManagerService主要管理用戶的賬號信息,運行中的用戶管理由ActivityManagerService實行。
用戶有四種運行狀態,定義如下:
[java] view plain copy public final class UserStartedState { // User is first coming up. public final static int STATE_BOOTING = 0;//用戶正在啟動 // User is in the normal running state. public final static int STATE_RUNNING = 1;//用戶正在運行 // User is in the initial process of being stopped. public final static int STATE_STOPPING = 2;//用戶正在停止 // User is in the final phase of stopping, sending Intent.ACTION_SHUTDOWN. public final static int STATE_SHUTDOWN = 3;//用戶已經關閉 在AMS中,變量mCurrentUserId記錄了當前的用戶Id,mStartedUsers列表則保存了正在運行的用戶,只有在mStartedUsers列表中的用戶才會有上面這4中狀態,不在列表中的是已經注冊了但是沒有運行的用戶。對消息START_USER_SWITCH_MSG的處理是調用方法showUserSwitchDialog,這個方法將彈出一個對話框來確定是否要進行用戶切換,一旦用戶點擊確定按鈕后,調用startUserInForeground()方法
[java] view plain copy case START_USER_SWITCH_MSG: { showUserSwitchDialog(msg.arg1, (String) msg.obj); break; } [java] view plain copy private void showUserSwitchDialog(int userId, String userName) { // The dialog will show and then initiate the user switch by calling startUserInForeground Dialog d = new UserSwitchingDialog(this, mContext, userId, userName, true /* above system */); d.show(); }startUserInForeground()方法如下:
[java] view plain copy /** * Start user, if its not already running, and bring it to foreground. */ boolean startUserInForeground(final int userId, Dialog dlg) { boolean result = startUser(userId, /* foreground */ true); dlg.dismiss(); return result; } 在startUserInForeground()方法中只是調用了startUser()方法,如下:[java] view plain copy private boolean startUser(final int userId, final boolean foreground) { if (checkCallingPermission(INTERACT_ACROSS_USERS_FULL) != PackageManager.PERMISSION_GRANTED) { 。。。。。。//檢查調用者權限 } final long ident = Binder.clearCallingIdentity(); try { synchronized (this) { final int oldUserId = mCurrentUserId; if (oldUserId == userId) { return true;//當前用戶已經是參數指定的用戶,退出 } mStackSupervisor.setLockTaskModeLocked(null, false, "startUser"); final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId); 。。。。。。//一些錯誤處理,返回false if (foreground) { mWindowManager.startFreezingScreen(R.anim.screen_user_exit, R.anim.screen_user_enter); } boolean needStart = false; // 如果用戶還沒有啟動,需要先啟動用戶,切換用戶狀態為正在啟動 if (mStartedUsers.get(userId) == null) { mStartedUsers.put(userId, new UserStartedState(new UserHandle(userId), false)); updateStartedUserArrayLocked(); needStart = true; } final Integer userIdInt = Integer.valueOf(userId); mUserLru.remove(userIdInt); mUserLru.add(userIdInt);//調整用戶在mUserLru列表中的位置,當前用戶位于最末的位置 if (foreground) {//前臺切換 mCurrentUserId = userId;//切換當前用戶 mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up updateCurrentProfileIdsLocked(); mWindowManager.setCurrentUser(userId, mCurrentProfileIds);//在WMS這設置當前用戶 mWindowManager.lockNow(null);//鎖上屏幕 } else { final Integer currentUserIdInt = Integer.valueOf(mCurrentUserId); updateCurrentProfileIdsLocked(); mWindowManager.setCurrentProfileIds(mCurrentProfileIds); mUserLru.remove(currentUserIdInt); mUserLru.add(currentUserIdInt); } final UserStartedState uss = mStartedUsers.get(userId); // Make sure user is in the started state. If it is currently // stopping, we need to knock that off. if (uss.mState == UserStartedState.STATE_STOPPING) { // 如果用戶的狀態為正在停止,切換為正在運行 uss.mState = UserStartedState.STATE_RUNNING; updateStartedUserArrayLocked(); needStart = true; } else if (uss.mState == UserStartedState.STATE_SHUTDOWN) { // 如果用戶的狀態為正在關閉,切換為正在啟動 uss.mState = UserStartedState.STATE_BOOTING; updateStartedUserArrayLocked(); needStart = true; } if (uss.mState == UserStartedState.STATE_BOOTING) { //如果用戶的狀態為正在啟動,發送消息SYSTEM_USER_START_MSG mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0)); } if (foreground) { mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId, oldUserId)); mHandler.removeMessages(REPORT_USER_SWITCH_MSG); mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG); mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG, oldUserId, userId, uss)); mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG, oldUserId, userId, uss), USER_SWITCH_TIMEOUT); } if (needStart) { // 如果切換前用戶不在STATE_RUNNING狀態,向該用戶發送廣播ACTION_USER_STARTED Intent intent = new Intent(Intent.ACTION_USER_STARTED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, false, false, MY_PID, Process.SYSTEM_UID, userId); } if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) { if (userId != UserHandle.USER_OWNER) {//如果用戶還沒初始化,向該用戶發送廣播ACTION_USER_INITIALIZE Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); broadcastIntentLocked(null, null, intent, null, new IIntentReceiver.Stub() { public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { onUserInitialized(uss, foreground, oldUserId, userId); } }, 0, null, null, null, AppOpsManager.OP_NONE, true, false, MY_PID, Process.SYSTEM_UID, userId); uss.initializing = true; } else {//否則直接調用UserManagerService的makeInitialized方法 getUserManagerLocked().makeInitialized(userInfo.id); } } if (foreground) { if (!uss.initializing) {//如果用戶已經初始化了,將它設為前臺用戶 moveUserToForeground(uss, oldUserId, userId); } } else {//否則先將用戶加入到mStartingBackgroundUser中 mStackSupervisor.startBackgroundUserLocked(userId, uss); } if (needStart) {//如果用戶不在STATE_RUNNING狀態,向所有用戶發送廣播ACTION_USER_STARTING Intent intent = new Intent(Intent.ACTION_USER_STARTING); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); broadcastIntentLocked(null, null, intent, null, new IIntentReceiver.Stub() { @Override public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) throws RemoteException { } }, 0, null, null, INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE, true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); } } } finally { Binder.restoreCallingIdentity(ident); } return true; } startUser()方法代碼較長。方法中先確定用戶的狀態,如果用戶還沒有運行,那么mStartedUsers列表中將不會有用戶的信息,因此,需要先創建表示用戶狀態的對象UserStartedState,然后加入到mStartedUsers列表中。同時還要調整用戶在mUserLru列表中的位置到最后一項。系統中同時運行的用戶數量最多為3個(目前的定義),mUserLru列表就是記錄用戶的登錄順序,在需要時會停止多余的用戶運行。對于已經在mStartedUsers列表中的用戶,首先判定它的狀態是否為STATE_RUNNING,如果不是,則需要完成狀態轉換,如果處于STATE_STOPPING狀態,直接轉換為STATE_RUNNING狀態,如果處于STATE_SHUTDOWN狀態,先轉換為STATE_BOOTING狀態。因此,switchUser()方法結束后用戶有可能處于STATE_BOOTING狀態,那么用戶什么時候會再切換到STATE_RUNNING狀態呢?稍候分析。
切換用戶的工作還包括設置記錄當前用戶id的變量mCurrentUserId,調用WMS的setCurrentUser()方法來改變當前用戶、鎖定當前的屏幕等。
除了這些工作,startUser()方法最重要的工作是廣播和用戶相關的Intent。因為用戶切換還牽扯到很多模塊,例如壁紙管理、輸入法、賬號管理模塊,他們都需要知道當前用戶已經發生了變化,然后做出相應的調整。這里要注意廣播的對象,有些是面向新用戶的,有些是面向舊用戶,有些面向所有用戶。
(1)如果切換前用戶不在STATE_RUNNING狀態,向該用戶發送廣播ACTION_USER_STARTED。
(2)id不為0的用戶如果還沒有初始化,向該用戶發送廣播ACTION_USER_INITIALIZE。
(3)調用sendUserSwitchBroadcastsLocked()方法向舊用戶發送廣播ACTION_USER_BACKGROUND,向新用戶發送廣播ACTION_USER_FOREGROUND,向所有用戶發送廣播ACTION_USER_SWUTCHED。
(4)如果切換前用戶不在STATE_RUNNING狀態,向所有用戶發送廣播ACTION_USER_STARTING。
此外,startUser()方法中還會發兩條消息:REPORT_USER_SWITCH_MSG和USER_SWITCH_TIMEOUT_MSG。其中REPORT_USER_SWITCH_MSG消息處理方法是dispatchUserSwitch,如下:
[java] view plain copy case REPORT_USER_SWITCH_MSG: { dispatchUserSwitch((UserStartedState) msg.obj, msg.arg1, msg.arg2); break; } [java] view plain copy void dispatchUserSwitch(final UserStartedState uss, final int oldUserId, final int newUserId) { final int N = mUserSwitchObservers.beginBroadcast(); if (N > 0) { final IRemoteCallback callback = new IRemoteCallback.Stub() { int mCount = 0; @Override public void sendResult(Bundle data) throws RemoteException { synchronized (ActivityManagerService.this) { if (mCurUserSwitchCallback == this) { mCount++;//如果收到一條返回結果的調用,mCount++ if (mCount == N) {//如果所有結果都返回了,發送繼續處理的消息 sendContinueUserSwitchLocked(uss, oldUserId, newUserId); } } } } }; synchronized (this) { uss.switching = true; mCurUserSwitchCallback = callback; } for (int i=0; i<N; i++) { try {//調用觀測對象的onUserSwitching方法 mUserSwitchObservers.getBroadcastItem(i).onUserSwitching( newUserId, callback); } catch (RemoteException e) { } } } else {//如果沒有觀測對象,直接調用sendContinueUserSwitchLocked synchronized (this) { sendContinueUserSwitchLocked(uss, oldUserId, newUserId); } } mUserSwitchObservers.finishBroadcast(); } dispatchUserSwitch()方法的主要作用是調用mUserSwitchObservers列表中IUserSwitchObserver對象的onUserSwitching()接口。如果系統中有模塊對用戶切換感興趣,可以調用AMS的registerUserSwitchObserver()接口來注冊一個觀測對象,這個對象將會保存到mUserSwitchObservers列表中。注冊觀測對象的模塊在它的回調接口onUserSwitching()的處理中需要在調用傳遞的參數對象callback的sendResult方法來通知AMS。我們看下上面代碼中的sendResult()方法,只有所有注冊者都調用了sendResult()方法,mCount最后才會等于N,這時才會調用sendContinueUserSwitchLocked方法來發送CONTINUE_USER_SWITCH_MSG消息來繼續切換用戶。我們看下USER_SWITCH_TIMEOUT_MSG消息的處理,發送USER_SWITCH_TIMEOUT_MSG消息是為了防止對CONTINUE_USER_SWITCH_MSG消息的處理時間過長,畢竟只有所有注冊者處理完成才能繼續。Android5.1不同于以往版本,這里會繼續發送CONTINUE_USER_SWITCH_MSG消息,繼續進行用戶切換。如下:
[java] view plain copy case USER_SWITCH_TIMEOUT_MSG: { timeoutUserSwitch((UserStartedState) msg.obj, msg.arg1, msg.arg2); break; } [java] view plain copy void timeoutUserSwitch(UserStartedState uss, int oldUserId, int newUserId) { synchronized (this) { Slog.w(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId); sendContinueUserSwitchLocked(uss, oldUserId, newUserId); } } [java] view plain copy void sendContinueUserSwitchLocked(UserStartedState uss, int oldUserId, int newUserId) { mCurUserSwitchCallback = null; mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG); mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG, oldUserId, newUserId, uss)); } [java] view plain copy case CONTINUE_USER_SWITCH_MSG: { continueUserSwitch((UserStartedState) msg.obj, msg.arg1, msg.arg2); break; } [java] view plain copy void continueUserSwitch(UserStartedState uss, int oldUserId, int newUserId) { completeSwitchAndInitalize(uss, newUserId, false, true); } [java] view plain copy void completeSwitchAndInitalize(UserStartedState uss, int newUserId, boolean clearInitializing, boolean clearSwitching) { boolean unfrozen = false; synchronized (this) { if (clearInitializing) { uss.initializing = false; getUserManagerLocked().makeInitialized(uss.mHandle.getIdentifier()); } if (clearSwitching) { uss.switching = false; } if (!uss.switching && !uss.initializing) { mWindowManager.stopFreezingScreen(); unfrozen = true; } } if (unfrozen) { final int N = mUserSwitchObservers.beginBroadcast(); for (int i=0; i<N; i++) { try { mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(newUserId); } catch (RemoteException e) { } } mUserSwitchObservers.finishBroadcast(); } stopGuestUserIfBackground(); } CONTINUE_USER_SWITCH_MSG消息的處理方法是continueUserSwitch(),這個方法會調用completeSwitchAndInitalize()方法繼續處理,處理的結果是對mUserSwitchObservers列表中所有觀測對象調用他們的onUserSwitchComplete()方法。至此,startUser()方法的工作就完成了。但是還有一個問題,狀態為STATE_BOOTING的用戶什么時候切換到STATE_RUNNING狀態?在activityIdleInternalLocked()方法中有一段代碼如下:
[java] view plain copy for (int i = 0; i < startingUsers.size(); i++) { mService.finishUserSwitch(startingUsers.get(i)); } activityIdleInternalLocked()方法會在Activity進入Idle狀態時調用,也就相當于用戶已經切換完成了,會對每個startingUsers列表中的用戶調用finishUserBoot()方法,如下:[java] view plain copy void finishUserSwitch(UserStartedState uss) { synchronized (this) { finishUserBoot(uss); startProfilesLocked(); int num = mUserLru.size(); int i = 0; while (num > MAX_RUNNING_USERS && i < mUserLru.size()) { Integer oldUserId = mUserLru.get(i); UserStartedState oldUss = mStartedUsers.get(oldUserId); if (oldUss == null) {//正常情況下不會出現為null // Shouldn't happen, but be sane if it does. mUserLru.remove(i); num--; continue; } if (oldUss.mState == UserStartedState.STATE_STOPPING || oldUss.mState == UserStartedState.STATE_SHUTDOWN) { // This user is already stopping, doesn't count.如果用戶已經停止了,繼續 num--; i++; continue; } if (oldUserId == UserHandle.USER_OWNER || oldUserId == mCurrentUserId) { // Owner and current can't be stopped, but count as running. i++;//如果是主用戶或當前用戶,繼續 continue; } // This is a user to be stopped.停止用戶 stopUserLocked(oldUserId, null); num--; i++; } } } finishUserSwitch()方法完成了兩件工作。一是調用方法finishUserBoot()把狀態還是STATE_BOOTING的用戶切換到STATE_RUNNING狀態,同時發送廣播ACTION_BOOT_COMPLETED給該用戶,表示用戶啟動結束。另一件工作是停止多余的用戶。從mUserLru列表的第0項開始,對于處于狀態STATE_RUNNING的用戶,只要不是主用戶或當前用戶,一律停止,只保留處于STATE_RUNNING狀態的用戶。ActivityManagerService中停止用戶運行的接口是stopUser(),這個方法在檢查了調用進程的權限后,調用內部方法stopUserLocked()繼續停止用戶的工作,stopUserLocked()代碼如下:
[java] view plain copy private int stopUserLocked(final int userId, final IStopUserCallback callback) { if (DEBUG_MU) Slog.i(TAG_MU, "stopUserLocked userId=" + userId); if (mCurrentUserId == userId && mTargetUserId == UserHandle.USER_NULL) { return ActivityManager.USER_OP_IS_CURRENT; }//如果要求停止的用戶是當前用戶,不能停止,返回 final UserStartedState uss = mStartedUsers.get(userId); if (uss == null) { // User is not started, nothing to do... but we do need to // callback if requested. if (callback != null) { mHandler.post(new Runnable() { @Override public void run() { try { callback.userStopped(userId);//回調方法userStopped } catch (RemoteException e) { } } }); } return ActivityManager.USER_OP_SUCCESS;//0 } if (callback != null) { uss.mStopCallbacks.add(callback); } if (uss.mState != UserStartedState.STATE_STOPPING && uss.mState != UserStartedState.STATE_SHUTDOWN) { uss.mState = UserStartedState.STATE_STOPPING;//將用戶的狀態切換為正在停止 updateStartedUserArrayLocked();//更新mStartedUserArray數組,存放UserStartedState對象 long ident = Binder.clearCallingIdentity(); try { // We are going to broadcast ACTION_USER_STOPPING and then // once that is done send a final ACTION_SHUTDOWN and then // stop the user. final Intent stoppingIntent = new Intent(Intent.ACTION_USER_STOPPING); stoppingIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); stoppingIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); stoppingIntent.putExtra(Intent.EXTRA_SHUTDOWN_USERSPACE_ONLY, true); final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN); // This is the result receiver for the final shutdown broadcast. final IIntentReceiver shutdownReceiver = new IIntentReceiver.Stub() { @Override public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { finishUserStop(uss);//收到ACTION_SHUTDOWN廣播,繼續執行 } }; // This is the result receiver for the initial stopping broadcast. final IIntentReceiver stoppingReceiver = new IIntentReceiver.Stub() { @Override public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { // On to the next. synchronized (ActivityManagerService.this) { if (uss.mState != UserStartedState.STATE_STOPPING) { // Whoops, we are being started back up. Abort, abort! return; } uss.mState = UserStartedState.STATE_SHUTDOWN;//收到ACTION_USER_STOPPING廣播后,改變用戶狀態 } mBatteryStatsService.noteEvent( BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH, Integer.toString(userId), userId); mSystemServiceManager.stopUser(userId); broadcastIntentLocked(null, null, shutdownIntent, null, shutdownReceiver, 0, null, null, null, AppOpsManager.OP_NONE, true, false, MY_PID, Process.SYSTEM_UID, userId);//再發送廣播shutdownIntent(Intent.ACTION_SHUTDOWN) } }; // Kick things off. broadcastIntentLocked(null, null, stoppingIntent, null, stoppingReceiver, 0, null, null, INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE, true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);//發送廣播stoppingIntent(Intent.ACTION_USER_STOPPING) } finally { Binder.restoreCallingIdentity(ident); } } return ActivityManager.USER_OP_SUCCESS; } stopUserLocked()方法首先判斷請求停止的用戶是否是當前用戶,是,返回。由此可見,當前正在運行的用戶是不能停止的,必須先切換到其他的用戶才能再停止該用戶。接下來判斷用戶是否處于運行狀態,沒有運行就post一個消息,在消息的處理方法中調用參數中的回調方法結束處理。
如果用戶已經運行,先切換用戶的狀態為STATE_STOPPING,然后廣播一個用戶正在停止的消息ACTION_USER_STOPPING,同時方法中也會接收這個廣播,收到后切換用戶的狀態為STATE_SHUTDOWN,再發出一個ACTION_SHUTDOWN的廣播,方法中同樣也會接受這個廣播,收到后再調用finishUserStop()方法繼續處理,如下:
[java] view plain copy void finishUserStop(UserStartedState uss) { final int userId = uss.mHandle.getIdentifier(); boolean stopped; ArrayList<IStopUserCallback> callbacks; synchronized (this) { callbacks = new ArrayList<IStopUserCallback>(uss.mStopCallbacks); if (mStartedUsers.get(userId) != uss) { stopped = false; } else if (uss.mState != UserStartedState.STATE_SHUTDOWN) { stopped = false; } else { stopped = true; // User can no longer run.從AMS的用戶管理的數據結構中刪除用戶 mStartedUsers.remove(userId); mUserLru.remove(Integer.valueOf(userId)); updateStartedUserArrayLocked(); // Clean up all state and processes associated with the user. // Kill all the processes for the user. forceStopUserLocked(userId, "finish user");//發送廣播Intent.ACTION_USER_STOPPED } // Explicitly remove the old information in mRecentTasks. removeRecentTasksForUserLocked(userId);//清除用戶相關的Recent Task列表 } for (int i=0; i<callbacks.size(); i++) { try {//調用UserManagerService的回調方法 if (stopped) callbacks.get(i).userStopped(userId); else callbacks.get(i).userStopAborted(userId); } catch (RemoteException e) { } } if (stopped) { mSystemServiceManager.cleanupUser(userId);//從mStackSupervisor刪除用戶 synchronized (this) { mStackSupervisor.removeUserLocked(userId); } } } finishUserStop()方法的主要工作是從mStartedUsers和mUserLru中刪除用戶,然后發送廣播Intent.ACTION_USER_STOPPED來通知某個用戶停止了,接下來調用UserManagerService的回調方法通知UserManagerService來處理,最后調用removeUserLocked()方法從mStackSupervisor列表中刪除用戶的相關信息。新聞熱點
疑難解答