diff options
| author | 2017-09-27 17:39:17 +0000 | |
|---|---|---|
| committer | 2017-09-27 17:39:17 +0000 | |
| commit | d1d969084b997886f6656c176792e718925955e0 (patch) | |
| tree | cb143e19ce4aa25aef3ce2a3e86d5991093d5df8 | |
| parent | 7c7a714be6a01550bd090d42812aa8b88671d55a (diff) | |
| parent | 1b3edacd6a381ae02e6b82cb9a4750ea8cb9ec84 (diff) | |
Merge "Use separate lock in UserController"
10 files changed, 679 insertions, 641 deletions
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 90ad8a5d0131..2131731de045 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -3150,7 +3150,7 @@ public final class ActiveServices { sr.userId, sr.crashCount, sr.shortName, app.pid); bringDownServiceLocked(sr); } else if (!allowRestart - || !mAm.mUserController.isUserRunningLocked(sr.userId, 0)) { + || !mAm.mUserController.isUserRunning(sr.userId, 0)) { bringDownServiceLocked(sr); } else { boolean canceled = scheduleServiceRestartLocked(sr, true); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 6df5fb7ccb0e..57bcda7a5646 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2519,10 +2519,12 @@ public class ActivityManagerService extends IActivityManager.Stub } public void setWindowManager(WindowManagerService wm) { - mWindowManager = wm; - mStackSupervisor.setWindowManager(wm); - mActivityStarter.setWindowManager(wm); - mLockTaskController.setWindowManager(wm); + synchronized (this) { + mWindowManager = wm; + mStackSupervisor.setWindowManager(wm); + mActivityStarter.setWindowManager(wm); + mLockTaskController.setWindowManager(wm); + } } public void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) { @@ -6150,7 +6152,7 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.w(TAG, "Failed trying to unstop package " + packageName + ": " + e); } - if (mUserController.isUserRunningLocked(user, 0)) { + if (mUserController.isUserRunning(user, 0)) { forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid); finishForceStopPackageLocked(packageName, pkgUid); } @@ -7308,33 +7310,32 @@ public class ActivityManagerService extends IActivityManager.Stub startProcessLocked(procs.get(ip), "on-hold", null); } } + if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) { + return; + } + // Start looking for apps that are abusing wake locks. + Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG); + mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL); + // Tell anyone interested that we are done booting! + SystemProperties.set("sys.boot_completed", "1"); - if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) { - // Start looking for apps that are abusing wake locks. - Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG); - mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL); - // Tell anyone interested that we are done booting! - SystemProperties.set("sys.boot_completed", "1"); - - // And trigger dev.bootcomplete if we are not showing encryption progress - if (!"trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt")) + // And trigger dev.bootcomplete if we are not showing encryption progress + if (!"trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt")) || "".equals(SystemProperties.get("vold.encrypt_progress"))) { - SystemProperties.set("dev.bootcomplete", "1"); - } - mUserController.sendBootCompletedLocked( - new IIntentReceiver.Stub() { - @Override - public void performReceive(Intent intent, int resultCode, - String data, Bundle extras, boolean ordered, - boolean sticky, int sendingUser) { - synchronized (ActivityManagerService.this) { - requestPssAllProcsLocked(SystemClock.uptimeMillis(), - true, false); - } - } - }); - mUserController.scheduleStartProfilesLocked(); + SystemProperties.set("dev.bootcomplete", "1"); } + mUserController.sendBootCompleted( + new IIntentReceiver.Stub() { + @Override + public void performReceive(Intent intent, int resultCode, + String data, Bundle extras, boolean ordered, + boolean sticky, int sendingUser) { + synchronized (ActivityManagerService.this) { + requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false); + } + } + }); + mUserController.scheduleStartProfiles(); } } @@ -11049,7 +11050,7 @@ public class ActivityManagerService extends IActivityManager.Stub boolean checkedGrants = false; if (checkUser) { // Looking for cross-user grants before enforcing the typical cross-users permissions - int tmpTargetUserId = mUserController.unsafeConvertIncomingUserLocked(userId); + int tmpTargetUserId = mUserController.unsafeConvertIncomingUser(userId); if (tmpTargetUserId != UserHandle.getUserId(callingUid)) { if (checkAuthorityGrants(callingUid, cpi, tmpTargetUserId, checkUser)) { return null; @@ -11437,7 +11438,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Make sure that the user who owns this provider is running. If not, // we don't want to allow it to run. - if (!mUserController.isUserRunningLocked(userId, 0)) { + if (!mUserController.isUserRunning(userId, 0)) { Slog.w(TAG, "Unable to launch app " + cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid + " for provider " @@ -12067,9 +12068,7 @@ public class ActivityManagerService extends IActivityManager.Stub int callingPid = Binder.getCallingPid(); long ident = 0; boolean clearedIdentity = false; - synchronized (this) { - userId = mUserController.unsafeConvertIncomingUserLocked(userId); - } + userId = mUserController.unsafeConvertIncomingUser(userId); if (canClearIdentity(callingPid, callingUid, userId)) { clearedIdentity = true; ident = Binder.clearCallingIdentity(); @@ -12487,7 +12486,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (mUserController.shouldConfirmCredentials(userId)) { if (mKeyguardController.isKeyguardLocked()) { // Showing launcher to avoid user entering credential twice. - final int currentUserId = mUserController.getCurrentUserIdLocked(); + final int currentUserId = mUserController.getCurrentUserId(); startHomeActivityLocked(currentUserId, "notifyLockedProfile"); } mStackSupervisor.lockAllProfileTasks(userId); @@ -14067,9 +14066,8 @@ public class ActivityManagerService extends IActivityManager.Stub } retrieveSettings(); - final int currentUserId; + final int currentUserId = mUserController.getCurrentUserId(); synchronized (this) { - currentUserId = mUserController.getCurrentUserIdLocked(); readGrantedUriPermissionsLocked(); } @@ -14148,7 +14146,7 @@ public class ActivityManagerService extends IActivityManager.Stub Binder.restoreCallingIdentity(ident); } mStackSupervisor.resumeFocusedStackTopActivityLocked(); - mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId); + mUserController.sendUserSwitchBroadcasts(-1, currentUserId); traceLog.traceEnd(); // ActivityManagerStartApps traceLog.traceEnd(); // PhaseActivityManagerReady } @@ -18976,7 +18974,7 @@ public class ActivityManagerService extends IActivityManager.Stub // If not, we will just skip it. Make an exception for shutdown broadcasts // and upgrade steps. - if (userId != UserHandle.USER_ALL && !mUserController.isUserRunningLocked(userId, 0)) { + if (userId != UserHandle.USER_ALL && !mUserController.isUserRunning(userId, 0)) { if ((callingUid != SYSTEM_UID || (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) && !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) { @@ -19395,7 +19393,7 @@ public class ActivityManagerService extends IActivityManager.Stub int[] users; if (userId == UserHandle.USER_ALL) { // Caller wants broadcast to go to all started users. - users = mUserController.getStartedUserArrayLocked(); + users = mUserController.getStartedUserArray(); } else { // Caller wants broadcast to go to one specific user. users = new int[] {userId}; @@ -20161,7 +20159,7 @@ public class ActivityManagerService extends IActivityManager.Stub void updateUserConfigurationLocked() { final Configuration configuration = new Configuration(getGlobalConfiguration()); - final int currentUserId = mUserController.getCurrentUserIdLocked(); + final int currentUserId = mUserController.getCurrentUserId(); Settings.System.adjustConfigurationForUser(mContext.getContentResolver(), configuration, currentUserId, Settings.System.canWrite(mContext)); updateConfigurationLocked(configuration, null /* starting */, false /* initLocale */, @@ -20272,7 +20270,7 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig); // TODO(multi-display): Update UsageEvents#Event to include displayId. mUsageStatsService.reportConfigurationChange(mTempConfig, - mUserController.getCurrentUserIdLocked()); + mUserController.getCurrentUserId()); // TODO: If our config changes, should we auto dismiss any currently showing dialogs? mShowDialogs = shouldShowDialogs(mTempConfig); @@ -22203,7 +22201,7 @@ public class ActivityManagerService extends IActivityManager.Stub String authority) { if (app == null) return; if (app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { - UserState userState = mUserController.getStartedUserStateLocked(app.userId); + UserState userState = mUserController.getStartedUserState(app.userId); if (userState == null) return; final long now = SystemClock.elapsedRealtime(); Long lastReported = userState.mProviderLastReportedFg.get(authority); @@ -23512,10 +23510,8 @@ public class ActivityManagerService extends IActivityManager.Stub } String getStartedUserState(int userId) { - synchronized (this) { - final UserState userState = mUserController.getStartedUserStateLocked(userId); - return UserState.stateToString(userState.state); - } + final UserState userState = mUserController.getStartedUserState(userId); + return UserState.stateToString(userState.state); } @Override @@ -23530,9 +23526,7 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.w(TAG, msg); throw new SecurityException(msg); } - synchronized (this) { - return mUserController.isUserRunningLocked(userId, flags); - } + return mUserController.isUserRunning(userId, flags); } @Override @@ -23546,9 +23540,7 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.w(TAG, msg); throw new SecurityException(msg); } - synchronized (this) { - return mUserController.getStartedUserArrayLocked(); - } + return mUserController.getStartedUserArray(); } @Override @@ -23569,9 +23561,7 @@ public class ActivityManagerService extends IActivityManager.Stub } public boolean isUserStopped(int userId) { - synchronized (this) { - return mUserController.getStartedUserStateLocked(userId) == null; - } + return mUserController.getStartedUserState(userId) == null; } ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) { @@ -24159,7 +24149,7 @@ public class ActivityManagerService extends IActivityManager.Stub permission.INTERACT_ACROSS_USERS_FULL, "getLastResumedActivityUserId()"); synchronized (this) { if (mLastResumedActivity == null) { - return mUserController.getCurrentUserIdLocked(); + return mUserController.getCurrentUserId(); } return mLastResumedActivity.userId; } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 2241ed6ebcad..193443691ee5 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -1948,7 +1948,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo return (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0 || (mStackSupervisor.isCurrentProfileLocked(userId) - && service.mUserController.isUserRunningLocked(userId, 0 /* flags */)); + && service.mUserController.isUserRunning(userId, 0 /* flags */)); } /** diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 63d31e230e92..829669327bf4 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -467,7 +467,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mHandler = new ActivityStackHandler(mService.mHandler.getLooper()); mWindowManager = mService.mWindowManager; mStackId = stackId; - mCurrentUser = mService.mUserController.getCurrentUserIdLocked(); + mCurrentUser = mService.mUserController.getCurrentUserId(); mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID ? new LaunchingTaskPositioner() : null; mTmpRect2.setEmpty(); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index bf0c3a4d261a..f423ce81bd75 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -3660,7 +3660,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D /** Checks whether the userid is a profile of the current user. */ boolean isCurrentProfileLocked(int userId) { if (userId == mCurrentUser) return true; - return mService.mUserController.isCurrentProfileLocked(userId); + return mService.mUserController.isCurrentProfile(userId); } /** diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 440b3d3baedd..fe380970c33b 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -507,7 +507,7 @@ class AppErrors { // launching the report UI under a different user. app.errorReportReceiver = null; - for (int userId : mService.mUserController.getCurrentProfileIdsLocked()) { + for (int userId : mService.mUserController.getCurrentProfileIds()) { if (app.userId == userId) { app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver( mContext, app.info.packageName, app.info.flags); @@ -728,7 +728,7 @@ class AppErrors { boolean isBackground = (UserHandle.getAppId(proc.uid) >= Process.FIRST_APPLICATION_UID && proc.pid != MY_PID); - for (int userId : mService.mUserController.getCurrentProfileIdsLocked()) { + for (int userId : mService.mUserController.getCurrentProfileIds()) { isBackground &= (proc.userId != userId); } if (isBackground && !showBackground) { diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index ee593866da68..7930f5340205 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -308,7 +308,7 @@ final class PendingIntentRecord extends IIntentSender.Stub { boolean sendFinish = finishedReceiver != null; int userId = key.userId; if (userId == UserHandle.USER_CURRENT) { - userId = owner.mUserController.getCurrentOrTargetUserIdLocked(); + userId = owner.mUserController.getCurrentOrTargetUserId(); } int res = 0; switch (key.type) { diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index f2e294217999..36f6b111d56a 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -103,6 +103,14 @@ import java.util.concurrent.atomic.AtomicInteger; /** * Helper class for {@link ActivityManagerService} responsible for multi-user functionality. + * + * <p>This class use {@link #mLock} to synchronize access to internal state. Methods that require + * {@link #mLock} to be held should have "LU" suffix in the name. + * + * <p><strong>Important:</strong> Synchronized code, i.e. one executed inside a synchronized(mLock) + * block or inside LU method, should only access internal state of this class or make calls to + * other LU methods. Non-LU method calls or calls to external classes are discouraged as they + * may cause lock inversion. */ class UserController implements Handler.Callback { private static final String TAG = TAG_WITH_CLASS_NAME ? "UserController" : TAG_AM; @@ -136,7 +144,9 @@ class UserController implements Handler.Callback { // when it never calls back. private static final int USER_SWITCH_CALLBACKS_TIMEOUT_MS = 5 * 1000; - private final Object mLock; + // Lock for internal state. + private final Object mLock = new Object(); + private final Injector mInjector; private final Handler mHandler; private final Handler mUiHandler; @@ -175,7 +185,8 @@ class UserController implements Handler.Callback { /** * Mapping from each known user ID to the profile group ID it is associated with. */ - private final SparseIntArray mUserProfileGroupIdsSelfLocked = new SparseIntArray(); + @GuardedBy("mLock") + private final SparseIntArray mUserProfileGroupIds = new SparseIntArray(); /** * Registered observers of the user switching mechanics. @@ -206,7 +217,6 @@ class UserController implements Handler.Callback { @VisibleForTesting UserController(Injector injector) { mInjector = injector; - mLock = injector.getLock(); mHandler = mInjector.getHandler(this); mUiHandler = mInjector.getUiHandler(this); // User 0 is the first and only user that runs at boot. @@ -214,19 +224,18 @@ class UserController implements Handler.Callback { mStartedUsers.put(UserHandle.USER_SYSTEM, uss); mUserLru.add(UserHandle.USER_SYSTEM); mLockPatternUtils = mInjector.getLockPatternUtils(); - updateStartedUserArrayLocked(); + updateStartedUserArrayLU(); } void finishUserSwitch(UserState uss) { + finishUserBoot(uss); + startProfiles(); synchronized (mLock) { - finishUserBoot(uss); - - startProfilesLocked(); - stopRunningUsersLocked(MAX_RUNNING_USERS); + stopRunningUsersLU(MAX_RUNNING_USERS); } } - void stopRunningUsersLocked(int maxRunningUsers) { + void stopRunningUsersLU(int maxRunningUsers) { int num = mUserLru.size(); int i = 0; while (num > maxRunningUsers && i < mUserLru.size()) { @@ -255,7 +264,7 @@ class UserController implements Handler.Callback { continue; } // This is a user to be stopped. - if (stopUsersLocked(oldUserId, false, null) != USER_OP_SUCCESS) { + if (stopUsersLU(oldUserId, false, null) != USER_OP_SUCCESS) { num--; } num--; @@ -273,55 +282,57 @@ class UserController implements Handler.Callback { Slog.d(TAG, "Finishing user boot " + userId); synchronized (mLock) { // Bail if we ended up with a stale user - if (mStartedUsers.get(userId) != uss) return; + if (mStartedUsers.get(userId) != uss) { + return; + } + } - // We always walk through all the user lifecycle states to send - // consistent developer events. We step into RUNNING_LOCKED here, - // but we might immediately step into RUNNING below if the user - // storage is already unlocked. - if (uss.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) { - mInjector.getUserManagerInternal().setUserState(userId, uss.state); - // Do not report secondary users, runtime restarts or first boot/upgrade - if (userId == UserHandle.USER_SYSTEM - && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) { - int uptimeSeconds = (int)(SystemClock.elapsedRealtime() / 1000); - MetricsLogger.histogram(mInjector.getContext(), - "framework_locked_boot_completed", uptimeSeconds); - final int MAX_UPTIME_SECONDS = 120; - if (uptimeSeconds > MAX_UPTIME_SECONDS) { - Slog.wtf("SystemServerTiming", - "finishUserBoot took too long. uptimeSeconds=" + uptimeSeconds); - } + // We always walk through all the user lifecycle states to send + // consistent developer events. We step into RUNNING_LOCKED here, + // but we might immediately step into RUNNING below if the user + // storage is already unlocked. + if (uss.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) { + mInjector.getUserManagerInternal().setUserState(userId, uss.state); + // Do not report secondary users, runtime restarts or first boot/upgrade + if (userId == UserHandle.USER_SYSTEM + && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) { + int uptimeSeconds = (int)(SystemClock.elapsedRealtime() / 1000); + MetricsLogger.histogram(mInjector.getContext(), + "framework_locked_boot_completed", uptimeSeconds); + final int MAX_UPTIME_SECONDS = 120; + if (uptimeSeconds > MAX_UPTIME_SECONDS) { + Slog.wtf("SystemServerTiming", + "finishUserBoot took too long. uptimeSeconds=" + uptimeSeconds); } + } - mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG, - userId, 0)); - Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null); - intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT - | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); - mInjector.broadcastIntentLocked(intent, null, resultTo, 0, null, null, - new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED }, - AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId); - } - - // We need to delay unlocking managed profiles until the parent user - // is also unlocked. - if (mInjector.getUserManager().isManagedProfile(userId)) { - final UserInfo parent = mInjector.getUserManager().getProfileParent(userId); - if (parent != null - && isUserRunningLocked(parent.id, ActivityManager.FLAG_AND_UNLOCKED)) { - Slog.d(TAG, "User " + userId + " (parent " + parent.id - + "): attempting unlock because parent is unlocked"); - maybeUnlockUser(userId); - } else { - String parentId = (parent == null) ? "<null>" : String.valueOf(parent.id); - Slog.d(TAG, "User " + userId + " (parent " + parentId - + "): delaying unlock because parent is locked"); - } - } else { + mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG, + userId, 0)); + Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null); + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT + | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + mInjector.broadcastIntent(intent, null, resultTo, 0, null, null, + new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED}, + AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId); + } + + // We need to delay unlocking managed profiles until the parent user + // is also unlocked. + if (mInjector.getUserManager().isManagedProfile(userId)) { + final UserInfo parent = mInjector.getUserManager().getProfileParent(userId); + if (parent != null + && isUserRunning(parent.id, ActivityManager.FLAG_AND_UNLOCKED)) { + Slog.d(TAG, "User " + userId + " (parent " + parent.id + + "): attempting unlock because parent is unlocked"); maybeUnlockUser(userId); + } else { + String parentId = (parent == null) ? "<null>" : String.valueOf(parent.id); + Slog.d(TAG, "User " + userId + " (parent " + parentId + + "): delaying unlock because parent is locked"); } + } else { + maybeUnlockUser(userId); } } @@ -331,34 +342,30 @@ class UserController implements Handler.Callback { */ private void finishUserUnlocking(final UserState uss) { final int userId = uss.mHandle.getIdentifier(); - boolean proceedWithUnlock = false; + // Only keep marching forward if user is actually unlocked + if (!StorageManager.isUserKeyUnlocked(userId)) return; synchronized (mLock) { // Bail if we ended up with a stale user if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return; - // Only keep marching forward if user is actually unlocked - if (!StorageManager.isUserKeyUnlocked(userId)) return; - - if (uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) { - mInjector.getUserManagerInternal().setUserState(userId, uss.state); - proceedWithUnlock = true; + // Do not proceed if unexpected state + if (!uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) { + return; } } + mInjector.getUserManagerInternal().setUserState(userId, uss.state); + uss.mUnlockProgress.start(); - if (proceedWithUnlock) { - uss.mUnlockProgress.start(); - - // Prepare app storage before we go any further - uss.mUnlockProgress.setProgress(5, - mInjector.getContext().getString(R.string.android_start_title)); - mInjector.getUserManager().onBeforeUnlockUser(userId); - uss.mUnlockProgress.setProgress(20); + // Prepare app storage before we go any further + uss.mUnlockProgress.setProgress(5, + mInjector.getContext().getString(R.string.android_start_title)); + mInjector.getUserManager().onBeforeUnlockUser(userId); + uss.mUnlockProgress.setProgress(20); - // Dispatch unlocked to system services; when fully dispatched, - // that calls through to the next "unlocked" phase - mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss) - .sendToTarget(); - } + // Dispatch unlocked to system services; when fully dispatched, + // that calls through to the next "unlocked" phase + mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss) + .sendToTarget(); } /** @@ -367,63 +374,63 @@ class UserController implements Handler.Callback { */ void finishUserUnlocked(final UserState uss) { final int userId = uss.mHandle.getIdentifier(); + // Only keep marching forward if user is actually unlocked + if (!StorageManager.isUserKeyUnlocked(userId)) return; synchronized (mLock) { // Bail if we ended up with a stale user if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return; - // Only keep marching forward if user is actually unlocked - if (!StorageManager.isUserKeyUnlocked(userId)) return; - - if (uss.setState(STATE_RUNNING_UNLOCKING, STATE_RUNNING_UNLOCKED)) { - mInjector.getUserManagerInternal().setUserState(userId, uss.state); - uss.mUnlockProgress.finish(); - - // Dispatch unlocked to external apps - final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED); - unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - unlockedIntent.addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); - mInjector.broadcastIntentLocked(unlockedIntent, null, null, 0, null, - null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, - userId); - - if (getUserInfo(userId).isManagedProfile()) { - UserInfo parent = mInjector.getUserManager().getProfileParent(userId); - if (parent != null) { - final Intent profileUnlockedIntent = new Intent( - Intent.ACTION_MANAGED_PROFILE_UNLOCKED); - profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); - profileUnlockedIntent.addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY + // Do not proceed if unexpected state + if (!uss.setState(STATE_RUNNING_UNLOCKING, STATE_RUNNING_UNLOCKED)) { + return; + } + } + mInjector.getUserManagerInternal().setUserState(userId, uss.state); + uss.mUnlockProgress.finish(); + // Dispatch unlocked to external apps + final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED); + unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + unlockedIntent.addFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); + mInjector.broadcastIntent(unlockedIntent, null, null, 0, null, + null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, + userId); + + if (getUserInfo(userId).isManagedProfile()) { + UserInfo parent = mInjector.getUserManager().getProfileParent(userId); + if (parent != null) { + final Intent profileUnlockedIntent = new Intent( + Intent.ACTION_MANAGED_PROFILE_UNLOCKED); + profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); + profileUnlockedIntent.addFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); - mInjector.broadcastIntentLocked(profileUnlockedIntent, - null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, - parent.id); - } - } + mInjector.broadcastIntent(profileUnlockedIntent, + null, null, 0, null, null, null, AppOpsManager.OP_NONE, + null, false, false, MY_PID, SYSTEM_UID, + parent.id); + } + } - // Send PRE_BOOT broadcasts if user fingerprint changed; we - // purposefully block sending BOOT_COMPLETED until after all - // PRE_BOOT receivers are finished to avoid ANR'ing apps - final UserInfo info = getUserInfo(userId); - if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) { - // Suppress double notifications for managed profiles that - // were unlocked automatically as part of their parent user - // being unlocked. - final boolean quiet; - if (info.isManagedProfile()) { - quiet = !uss.tokenProvided - || !mLockPatternUtils.isSeparateProfileChallengeEnabled(userId); - } else { - quiet = false; - } - mInjector.sendPreBootBroadcast(userId, quiet, - () -> finishUserUnlockedCompleted(uss)); - } else { - finishUserUnlockedCompleted(uss); - } + // Send PRE_BOOT broadcasts if user fingerprint changed; we + // purposefully block sending BOOT_COMPLETED until after all + // PRE_BOOT receivers are finished to avoid ANR'ing apps + final UserInfo info = getUserInfo(userId); + if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) { + // Suppress double notifications for managed profiles that + // were unlocked automatically as part of their parent user + // being unlocked. + final boolean quiet; + if (info.isManagedProfile()) { + quiet = !uss.tokenProvided + || !mLockPatternUtils.isSeparateProfileChallengeEnabled(userId); + } else { + quiet = false; } + mInjector.sendPreBootBroadcast(userId, quiet, + () -> finishUserUnlockedCompleted(uss)); + } else { + finishUserUnlockedCompleted(uss); } } @@ -432,60 +439,59 @@ class UserController implements Handler.Callback { synchronized (mLock) { // Bail if we ended up with a stale user if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return; - final UserInfo userInfo = getUserInfo(userId); - if (userInfo == null) { - return; - } + } + UserInfo userInfo = getUserInfo(userId); + if (userInfo == null) { + return; + } + // Only keep marching forward if user is actually unlocked + if (!StorageManager.isUserKeyUnlocked(userId)) return; - // Only keep marching forward if user is actually unlocked - if (!StorageManager.isUserKeyUnlocked(userId)) return; - - // Remember that we logged in - mInjector.getUserManager().onUserLoggedIn(userId); - - if (!userInfo.isInitialized()) { - if (userId != UserHandle.USER_SYSTEM) { - Slog.d(TAG, "Initializing user #" + userId); - Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE); - intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND - | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); - mInjector.broadcastIntentLocked(intent, null, - new IIntentReceiver.Stub() { - @Override - public void performReceive(Intent intent, int resultCode, - String data, Bundle extras, boolean ordered, - boolean sticky, int sendingUser) { - // Note: performReceive is called with mService lock held - mInjector.getUserManager().makeInitialized(userInfo.id); - } - }, 0, null, null, null, AppOpsManager.OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, userId); - } - } + // Remember that we logged in + mInjector.getUserManager().onUserLoggedIn(userId); - Slog.i(TAG, "Sending BOOT_COMPLETE user #" + userId); - // Do not report secondary users, runtime restarts or first boot/upgrade - if (userId == UserHandle.USER_SYSTEM - && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) { - int uptimeSeconds = (int) (SystemClock.elapsedRealtime() / 1000); - MetricsLogger.histogram(mInjector.getContext(), "framework_boot_completed", - uptimeSeconds); + if (!userInfo.isInitialized()) { + if (userId != UserHandle.USER_SYSTEM) { + Slog.d(TAG, "Initializing user #" + userId); + Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE); + intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND + | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + mInjector.broadcastIntent(intent, null, + new IIntentReceiver.Stub() { + @Override + public void performReceive(Intent intent, int resultCode, + String data, Bundle extras, boolean ordered, + boolean sticky, int sendingUser) { + // Note: performReceive is called with mService lock held + mInjector.getUserManager().makeInitialized(userInfo.id); + } + }, 0, null, null, null, AppOpsManager.OP_NONE, + null, true, false, MY_PID, SYSTEM_UID, userId); } - final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null); - bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT - | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); - mInjector.broadcastIntentLocked(bootIntent, null, new IIntentReceiver.Stub() { - @Override - public void performReceive(Intent intent, int resultCode, String data, - Bundle extras, boolean ordered, boolean sticky, int sendingUser) - throws RemoteException { - Slog.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u" + userId); - } - }, 0, null, null, - new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED }, - AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId); } + + Slog.i(TAG, "Sending BOOT_COMPLETE user #" + userId); + // Do not report secondary users, runtime restarts or first boot/upgrade + if (userId == UserHandle.USER_SYSTEM + && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) { + int uptimeSeconds = (int) (SystemClock.elapsedRealtime() / 1000); + MetricsLogger.histogram(mInjector.getContext(), "framework_boot_completed", + uptimeSeconds); + } + final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null); + bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT + | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + mInjector.broadcastIntent(bootIntent, null, new IIntentReceiver.Stub() { + @Override + public void performReceive(Intent intent, int resultCode, String data, + Bundle extras, boolean ordered, boolean sticky, int sendingUser) + throws RemoteException { + Slog.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u" + userId); + } + }, 0, null, null, + new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED}, + AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId); } int restartUser(final int userId, final boolean foreground) { @@ -516,33 +522,33 @@ class UserController implements Handler.Callback { } enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId); synchronized (mLock) { - return stopUsersLocked(userId, force, callback); + return stopUsersLU(userId, force, callback); } } /** * Stops the user along with its related users. The method calls - * {@link #getUsersToStopLocked(int)} to determine the list of users that should be stopped. + * {@link #getUsersToStopLU(int)} to determine the list of users that should be stopped. */ - private int stopUsersLocked(final int userId, boolean force, final IStopUserCallback callback) { + private int stopUsersLU(final int userId, boolean force, final IStopUserCallback callback) { if (userId == UserHandle.USER_SYSTEM) { return USER_OP_ERROR_IS_SYSTEM; } - if (isCurrentUserLocked(userId)) { + if (isCurrentUserLU(userId)) { return USER_OP_IS_CURRENT; } - int[] usersToStop = getUsersToStopLocked(userId); + int[] usersToStop = getUsersToStopLU(userId); // If one of related users is system or current, no related users should be stopped for (int i = 0; i < usersToStop.length; i++) { int relatedUserId = usersToStop[i]; - if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLocked(relatedUserId)) { + if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLU(relatedUserId)) { if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked cannot stop related user " + relatedUserId); // We still need to stop the requested user if it's a force stop. if (force) { Slog.i(TAG, "Force stop user " + userId + ". Related users will not be stopped"); - stopSingleUserLocked(userId, callback); + stopSingleUserLU(userId, callback); return USER_OP_SUCCESS; } return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP; @@ -550,25 +556,22 @@ class UserController implements Handler.Callback { } if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop)); for (int userIdToStop : usersToStop) { - stopSingleUserLocked(userIdToStop, userIdToStop == userId ? callback : null); + stopSingleUserLU(userIdToStop, userIdToStop == userId ? callback : null); } return USER_OP_SUCCESS; } - private void stopSingleUserLocked(final int userId, final IStopUserCallback callback) { + private void stopSingleUserLU(final int userId, final IStopUserCallback callback) { if (DEBUG_MU) Slog.i(TAG, "stopSingleUserLocked userId=" + userId); final UserState 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); - } catch (RemoteException e) { - } + mHandler.post(() -> { + try { + callback.userStopped(userId); + } catch (RemoteException e) { } }); } @@ -583,10 +586,10 @@ class UserController implements Handler.Callback { && uss.state != UserState.STATE_SHUTDOWN) { uss.setState(UserState.STATE_STOPPING); mInjector.getUserManagerInternal().setUserState(userId, uss.state); - updateStartedUserArrayLocked(); + updateStartedUserArrayLU(); - long ident = Binder.clearCallingIdentity(); - try { + // Post to handler to obtain amLock + mHandler.post(() -> { // We are going to broadcast ACTION_USER_STOPPING and then // once that is done send a final ACTION_SHUTDOWN and then // stop the user. @@ -599,24 +602,18 @@ class UserController implements Handler.Callback { @Override public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { - mHandler.post(new Runnable() { - @Override - public void run() { - finishUserStopping(userId, uss); - } - }); + mHandler.post(() -> finishUserStopping(userId, uss)); } }; + // Clear broadcast queue for the user to avoid delivering stale broadcasts - mInjector.clearBroadcastQueueForUserLocked(userId); + mInjector.clearBroadcastQueueForUser(userId); // Kick things off. - mInjector.broadcastIntentLocked(stoppingIntent, + mInjector.broadcastIntent(stoppingIntent, null, stoppingReceiver, 0, null, null, new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); - } finally { - Binder.restoreCallingIdentity(ident); - } + }); } } @@ -652,18 +649,17 @@ class UserController implements Handler.Callback { Integer.toString(userId), userId); mInjector.getSystemServiceManager().stopUser(userId); - synchronized (mLock) { - mInjector.broadcastIntentLocked(shutdownIntent, - null, shutdownReceiver, 0, null, null, null, - AppOpsManager.OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, userId); - } + mInjector.broadcastIntent(shutdownIntent, + null, shutdownReceiver, 0, null, null, null, + AppOpsManager.OP_NONE, + null, true, false, MY_PID, SYSTEM_UID, userId); } void finishUserStopped(UserState uss) { final int userId = uss.mHandle.getIdentifier(); boolean stopped; ArrayList<IStopUserCallback> callbacks; + boolean forceStopUser = false; synchronized (mLock) { callbacks = new ArrayList<>(uss.mStopCallbacks); if (mStartedUsers.get(userId) != uss) { @@ -674,16 +670,18 @@ class UserController implements Handler.Callback { stopped = true; // User can no longer run. mStartedUsers.remove(userId); - mInjector.getUserManagerInternal().removeUserState(userId); mUserLru.remove(Integer.valueOf(userId)); - updateStartedUserArrayLocked(); - - mInjector.activityManagerOnUserStopped(userId); - // Clean up all state and processes associated with the user. - // Kill all the processes for the user. - forceStopUserLocked(userId, "finish user"); + updateStartedUserArrayLU(); + forceStopUser = true; } } + if (forceStopUser) { + mInjector.getUserManagerInternal().removeUserState(userId); + mInjector.activityManagerOnUserStopped(userId); + // Clean up all state and processes associated with the user. + // Kill all the processes for the user. + forceStopUser(userId, "finish user"); + } for (int i = 0; i < callbacks.size(); i++) { try { @@ -695,9 +693,7 @@ class UserController implements Handler.Callback { if (stopped) { mInjector.systemServiceManagerCleanupUser(userId); - synchronized (mLock) { - mInjector.getActivityStackSupervisor().removeUserLocked(userId); - } + mInjector.stackSupervisorRemoveUser(userId); // Remove the user if it is ephemeral. if (getUserInfo(userId).isEphemeral()) { mInjector.getUserManager().removeUser(userId); @@ -715,39 +711,36 @@ class UserController implements Handler.Callback { * Determines the list of users that should be stopped together with the specified * {@code userId}. The returned list includes {@code userId}. */ - private @NonNull int[] getUsersToStopLocked(int userId) { + private @NonNull int[] getUsersToStopLU(int userId) { int startedUsersSize = mStartedUsers.size(); IntArray userIds = new IntArray(); userIds.add(userId); - synchronized (mUserProfileGroupIdsSelfLocked) { - int userGroupId = mUserProfileGroupIdsSelfLocked.get(userId, + int userGroupId = mUserProfileGroupIds.get(userId, UserInfo.NO_PROFILE_GROUP_ID); + for (int i = 0; i < startedUsersSize; i++) { + UserState uss = mStartedUsers.valueAt(i); + int startedUserId = uss.mHandle.getIdentifier(); + // Skip unrelated users (profileGroupId mismatch) + int startedUserGroupId = mUserProfileGroupIds.get(startedUserId, UserInfo.NO_PROFILE_GROUP_ID); - for (int i = 0; i < startedUsersSize; i++) { - UserState uss = mStartedUsers.valueAt(i); - int startedUserId = uss.mHandle.getIdentifier(); - // Skip unrelated users (profileGroupId mismatch) - int startedUserGroupId = mUserProfileGroupIdsSelfLocked.get(startedUserId, - UserInfo.NO_PROFILE_GROUP_ID); - boolean sameGroup = (userGroupId != UserInfo.NO_PROFILE_GROUP_ID) - && (userGroupId == startedUserGroupId); - // userId has already been added - boolean sameUserId = startedUserId == userId; - if (!sameGroup || sameUserId) { - continue; - } - userIds.add(startedUserId); + boolean sameGroup = (userGroupId != UserInfo.NO_PROFILE_GROUP_ID) + && (userGroupId == startedUserGroupId); + // userId has already been added + boolean sameUserId = startedUserId == userId; + if (!sameGroup || sameUserId) { + continue; } + userIds.add(startedUserId); } return userIds.toArray(); } - private void forceStopUserLocked(int userId, String reason) { - mInjector.activityManagerForceStopPackageLocked(userId, reason); + private void forceStopUser(int userId, String reason) { + mInjector.activityManagerForceStopPackage(userId, reason); Intent intent = new Intent(Intent.ACTION_USER_STOPPED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - mInjector.broadcastIntentLocked(intent, + mInjector.broadcastIntent(intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); } @@ -756,6 +749,7 @@ class UserController implements Handler.Callback { * Stops the guest or ephemeral user if it has gone to the background. */ private void stopGuestOrEphemeralUserIfBackground() { + IntArray userIds = new IntArray(); synchronized (mLock) { final int num = mUserLru.size(); for (int i = 0; i < num; i++) { @@ -766,35 +760,42 @@ class UserController implements Handler.Callback { || oldUss.state == UserState.STATE_SHUTDOWN) { continue; } - UserInfo userInfo = getUserInfo(oldUserId); - if (userInfo.isEphemeral()) { - LocalServices.getService(UserManagerInternal.class) - .onEphemeralUserStop(oldUserId); - } - if (userInfo.isGuest() || userInfo.isEphemeral()) { - // This is a user to be stopped. - stopUsersLocked(oldUserId, true, null); - break; + userIds.add(oldUserId); + } + } + final int userIdsSize = userIds.size(); + for (int i = 0; i < userIdsSize; i++) { + int oldUserId = userIds.get(i); + UserInfo userInfo = getUserInfo(oldUserId); + if (userInfo.isEphemeral()) { + LocalServices.getService(UserManagerInternal.class).onEphemeralUserStop(oldUserId); + } + if (userInfo.isGuest() || userInfo.isEphemeral()) { + // This is a user to be stopped. + synchronized (mLock) { + stopUsersLU(oldUserId, true, null); } + break; } } } - void scheduleStartProfilesLocked() { + void scheduleStartProfiles() { if (!mHandler.hasMessages(START_PROFILES_MSG)) { mHandler.sendMessageDelayed(mHandler.obtainMessage(START_PROFILES_MSG), DateUtils.SECOND_IN_MILLIS); } } - void startProfilesLocked() { + void startProfiles() { + int currentUserId = getCurrentUserId(); if (DEBUG_MU) Slog.i(TAG, "startProfilesLocked"); List<UserInfo> profiles = mInjector.getUserManager().getProfiles( - mCurrentUserId, false /* enabledOnly */); + currentUserId, false /* enabledOnly */); List<UserInfo> profilesToStart = new ArrayList<>(profiles.size()); for (UserInfo user : profiles) { if ((user.flags & UserInfo.FLAG_INITIALIZED) == UserInfo.FLAG_INITIALIZED - && user.id != mCurrentUserId && !user.isQuietModeEnabled()) { + && user.id != currentUserId && !user.isQuietModeEnabled()) { profilesToStart.add(user); } } @@ -856,143 +857,156 @@ class UserController implements Handler.Callback { final long ident = Binder.clearCallingIdentity(); try { - synchronized (mLock) { - final int oldUserId = mCurrentUserId; - if (oldUserId == userId) { - return true; - } + final int oldUserId = getCurrentUserId(); + if (oldUserId == userId) { + return true; + } - if (foreground) { - // TODO: I don't think this does what the caller think it does. Seems to only - // remove one locked task and won't work if multiple locked tasks are present. - mInjector.getLockTaskController().clearLockTaskMode("startUser"); - } + if (foreground) { + // TODO: I don't think this does what the caller think it does. Seems to only + // remove one locked task and won't work if multiple locked tasks are present. + mInjector.clearLockTaskMode("startUser"); + } - final UserInfo userInfo = getUserInfo(userId); - if (userInfo == null) { - Slog.w(TAG, "No user info for user #" + userId); - return false; - } - if (foreground && userInfo.isManagedProfile()) { - Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user"); - return false; - } + final UserInfo userInfo = getUserInfo(userId); + if (userInfo == null) { + Slog.w(TAG, "No user info for user #" + userId); + return false; + } + if (foreground && userInfo.isManagedProfile()) { + Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user"); + return false; + } - if (foreground && mUserSwitchUiEnabled) { - mInjector.getWindowManager().startFreezingScreen( - R.anim.screen_user_exit, R.anim.screen_user_enter); - } + if (foreground && mUserSwitchUiEnabled) { + mInjector.getWindowManager().startFreezingScreen( + R.anim.screen_user_exit, R.anim.screen_user_enter); + } - boolean needStart = false; + boolean needStart = false; + boolean updateUmState = false; + UserState uss; - // If the user we are switching to is not currently started, then - // we need to start it now. - if (mStartedUsers.get(userId) == null) { - UserState userState = new UserState(UserHandle.of(userId)); - mStartedUsers.put(userId, userState); - mInjector.getUserManagerInternal().setUserState(userId, userState.state); - updateStartedUserArrayLocked(); + // If the user we are switching to is not currently started, then + // we need to start it now. + synchronized (mLock) { + uss = mStartedUsers.get(userId); + if (uss == null) { + uss = new UserState(UserHandle.of(userId)); + mStartedUsers.put(userId, uss); + updateStartedUserArrayLU(); needStart = true; + updateUmState = true; } - - final UserState uss = mStartedUsers.get(userId); final Integer userIdInt = userId; mUserLru.remove(userIdInt); mUserLru.add(userIdInt); - - if (foreground) { + } + if (updateUmState) { + mInjector.getUserManagerInternal().setUserState(userId, uss.state); + } + if (foreground) { + synchronized (mLock) { mCurrentUserId = userId; - mInjector.updateUserConfigurationLocked(); mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up - updateCurrentProfileIdsLocked(); - mInjector.getWindowManager().setCurrentUser(userId, mCurrentProfileIds); - // Once the internal notion of the active user has switched, we lock the device - // with the option to show the user switcher on the keyguard. - if (mUserSwitchUiEnabled) { - mInjector.getWindowManager().setSwitchingUser(true); - mInjector.getWindowManager().lockNow(null); - } - } else { - final Integer currentUserIdInt = mCurrentUserId; - updateCurrentProfileIdsLocked(); - mInjector.getWindowManager().setCurrentProfileIds(mCurrentProfileIds); + } + mInjector.updateUserConfiguration(); + updateCurrentProfileIds(); + mInjector.getWindowManager().setCurrentUser(userId, getCurrentProfileIds()); + // Once the internal notion of the active user has switched, we lock the device + // with the option to show the user switcher on the keyguard. + if (mUserSwitchUiEnabled) { + mInjector.getWindowManager().setSwitchingUser(true); + mInjector.getWindowManager().lockNow(null); + } + } else { + final Integer currentUserIdInt = mCurrentUserId; + updateCurrentProfileIds(); + mInjector.getWindowManager().setCurrentProfileIds(getCurrentProfileIds()); + synchronized (mLock) { mUserLru.remove(currentUserIdInt); mUserLru.add(currentUserIdInt); } + } - // Make sure user is in the started state. If it is currently - // stopping, we need to knock that off. - if (uss.state == UserState.STATE_STOPPING) { - // If we are stopping, we haven't sent ACTION_SHUTDOWN, - // so we can just fairly silently bring the user back from - // the almost-dead. - uss.setState(uss.lastState); - mInjector.getUserManagerInternal().setUserState(userId, uss.state); - updateStartedUserArrayLocked(); - needStart = true; - } else if (uss.state == UserState.STATE_SHUTDOWN) { - // This means ACTION_SHUTDOWN has been sent, so we will - // need to treat this as a new boot of the user. - uss.setState(UserState.STATE_BOOTING); - mInjector.getUserManagerInternal().setUserState(userId, uss.state); - updateStartedUserArrayLocked(); - needStart = true; + // Make sure user is in the started state. If it is currently + // stopping, we need to knock that off. + if (uss.state == UserState.STATE_STOPPING) { + // If we are stopping, we haven't sent ACTION_SHUTDOWN, + // so we can just fairly silently bring the user back from + // the almost-dead. + uss.setState(uss.lastState); + mInjector.getUserManagerInternal().setUserState(userId, uss.state); + synchronized (mLock) { + updateStartedUserArrayLU(); + } + needStart = true; + } else if (uss.state == UserState.STATE_SHUTDOWN) { + // This means ACTION_SHUTDOWN has been sent, so we will + // need to treat this as a new boot of the user. + uss.setState(UserState.STATE_BOOTING); + mInjector.getUserManagerInternal().setUserState(userId, uss.state); + synchronized (mLock) { + updateStartedUserArrayLU(); } + needStart = true; + } - if (uss.state == UserState.STATE_BOOTING) { - // Give user manager a chance to propagate user restrictions - // to other services and prepare app storage - mInjector.getUserManager().onBeforeStartUser(userId); + if (uss.state == UserState.STATE_BOOTING) { + // Give user manager a chance to propagate user restrictions + // to other services and prepare app storage + mInjector.getUserManager().onBeforeStartUser(userId); - // Booting up a new user, need to tell system services about it. - // Note that this is on the same handler as scheduling of broadcasts, - // which is important because it needs to go first. - mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0)); - } + // Booting up a new user, need to tell system services about it. + // Note that this is on the same handler as scheduling of broadcasts, + // which is important because it needs to go first. + 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_MS); - } + 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_MS); + } - if (needStart) { - // Send USER_STARTED broadcast - 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); - mInjector.broadcastIntentLocked(intent, - null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, userId); - } + if (needStart) { + // Send USER_STARTED broadcast + 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); + mInjector.broadcastIntent(intent, + null, null, 0, null, null, null, AppOpsManager.OP_NONE, + null, false, false, MY_PID, SYSTEM_UID, userId); + } - if (foreground) { - moveUserToForegroundLocked(uss, oldUserId, userId); - } else { - finishUserBoot(uss); - } + if (foreground) { + moveUserToForeground(uss, oldUserId, userId); + } else { + finishUserBoot(uss); + } - if (needStart) { - Intent intent = new Intent(Intent.ACTION_USER_STARTING); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - mInjector.broadcastIntentLocked(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, - new String[] {INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); - } + if (needStart) { + Intent intent = new Intent(Intent.ACTION_USER_STARTING); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + mInjector.broadcastIntent(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, + new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE, + null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); } } finally { Binder.restoreCallingIdentity(ident); @@ -1036,7 +1050,7 @@ class UserController implements Handler.Callback { * when the the credential-encrypted storage isn't tied to a user-provided * PIN or pattern. */ - boolean maybeUnlockUser(final int userId) { + private boolean maybeUnlockUser(final int userId) { // Try unlocking storage using empty token return unlockUserCleared(userId, null, null, null); } @@ -1049,54 +1063,53 @@ class UserController implements Handler.Callback { } } - boolean unlockUserCleared(final int userId, byte[] token, byte[] secret, + private boolean unlockUserCleared(final int userId, byte[] token, byte[] secret, IProgressListener listener) { UserState uss; - synchronized (mLock) { - // TODO Move this block outside of synchronized if it causes lock contention - if (!StorageManager.isUserKeyUnlocked(userId)) { - final UserInfo userInfo = getUserInfo(userId); - final IStorageManager storageManager = getStorageManager(); - try { - // We always want to unlock user storage, even user is not started yet - storageManager.unlockUserKey(userId, userInfo.serialNumber, token, secret); - } catch (RemoteException | RuntimeException e) { - Slog.w(TAG, "Failed to unlock: " + e.getMessage()); - } + if (!StorageManager.isUserKeyUnlocked(userId)) { + final UserInfo userInfo = getUserInfo(userId); + final IStorageManager storageManager = getStorageManager(); + try { + // We always want to unlock user storage, even user is not started yet + storageManager.unlockUserKey(userId, userInfo.serialNumber, token, secret); + } catch (RemoteException | RuntimeException e) { + Slog.w(TAG, "Failed to unlock: " + e.getMessage()); } - // Bail if user isn't actually running, otherwise register the given - // listener to watch for unlock progress + } + synchronized (mLock) { + // Register the given listener to watch for unlock progress uss = mStartedUsers.get(userId); - if (uss == null) { - notifyFinished(userId, listener); - return false; - } else { + if (uss != null) { uss.mUnlockProgress.addListener(listener); uss.tokenProvided = (token != null); } } + // Bail if user isn't actually running + if (uss == null) { + notifyFinished(userId, listener); + return false; + } finishUserUnlocking(uss); - final ArraySet<Integer> childProfilesToUnlock = new ArraySet<>(); - synchronized (mLock) { + // We just unlocked a user, so let's now attempt to unlock any + // managed profiles under that user. - // We just unlocked a user, so let's now attempt to unlock any - // managed profiles under that user. - for (int i = 0; i < mStartedUsers.size(); i++) { - final int testUserId = mStartedUsers.keyAt(i); - final UserInfo parent = mInjector.getUserManager().getProfileParent(testUserId); - if (parent != null && parent.id == userId && testUserId != userId) { - Slog.d(TAG, "User " + testUserId + " (parent " + parent.id - + "): attempting unlock because parent was just unlocked"); - childProfilesToUnlock.add(testUserId); - } + // First, get list of userIds. Requires mLock, so we cannot make external calls, e.g. to UMS + int[] userIds; + synchronized (mLock) { + userIds = new int[mStartedUsers.size()]; + for (int i = 0; i < userIds.length; i++) { + userIds[i] = mStartedUsers.keyAt(i); } } - - final int size = childProfilesToUnlock.size(); - for (int i = 0; i < size; i++) { - maybeUnlockUser(childProfilesToUnlock.valueAt(i)); + for (int testUserId : userIds) { + final UserInfo parent = mInjector.getUserManager().getProfileParent(testUserId); + if (parent != null && parent.id == userId && testUserId != userId) { + Slog.d(TAG, "User " + testUserId + " (parent " + parent.id + + "): attempting unlock because parent was just unlocked"); + maybeUnlockUser(testUserId); + } } return true; @@ -1104,33 +1117,31 @@ class UserController implements Handler.Callback { boolean switchUser(final int targetUserId) { enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, targetUserId); - int currentUserId; - UserInfo targetUserInfo; + int currentUserId = getCurrentUserId(); + UserInfo targetUserInfo = getUserInfo(targetUserId); + if (targetUserId == currentUserId) { + Slog.i(TAG, "user #" + targetUserId + " is already the current user"); + return true; + } + if (targetUserInfo == null) { + Slog.w(TAG, "No user info for user #" + targetUserId); + return false; + } + if (!targetUserInfo.isDemo() && UserManager.isDeviceInDemoMode(mInjector.getContext())) { + Slog.w(TAG, "Cannot switch to non-demo user #" + targetUserId + + " when device is in demo mode"); + return false; + } + if (!targetUserInfo.supportsSwitchTo()) { + Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported"); + return false; + } + if (targetUserInfo.isManagedProfile()) { + Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user"); + return false; + } synchronized (mLock) { - currentUserId = getCurrentUserIdLocked(); - targetUserInfo = getUserInfo(targetUserId); - if (targetUserId == currentUserId) { - Slog.i(TAG, "user #" + targetUserId + " is already the current user"); - return true; - } - if (targetUserInfo == null) { - Slog.w(TAG, "No user info for user #" + targetUserId); - return false; - } - if (!targetUserInfo.isDemo() && UserManager.isDeviceInDemoMode(mInjector.getContext())) { - Slog.w(TAG, "Cannot switch to non-demo user #" + targetUserId - + " when device is in demo mode"); - return false; - } - if (!targetUserInfo.supportsSwitchTo()) { - Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported"); - return false; - } - if (targetUserInfo.isManagedProfile()) { - Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user"); - return false; - } - setTargetUserIdLocked(targetUserId); + mTargetUserId = targetUserId; } if (mUserSwitchUiEnabled) { UserInfo currentUserInfo = getUserInfo(currentUserId); @@ -1146,12 +1157,12 @@ class UserController implements Handler.Callback { return true; } - void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) { + private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) { // The dialog will show and then initiate the user switch by calling startUserInForeground mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second); } - void dispatchForegroundProfileChanged(int userId) { + private void dispatchForegroundProfileChanged(int userId) { final int observerCount = mUserSwitchObservers.beginBroadcast(); for (int i = 0; i < observerCount; i++) { try { @@ -1176,7 +1187,7 @@ class UserController implements Handler.Callback { mUserSwitchObservers.finishBroadcast(); } - void dispatchLockedBootComplete(int userId) { + private void dispatchLockedBootComplete(int userId) { final int observerCount = mUserSwitchObservers.beginBroadcast(); for (int i = 0; i < observerCount; i++) { try { @@ -1202,23 +1213,23 @@ class UserController implements Handler.Callback { synchronized (mLock) { if (DEBUG_MU) Slog.i(TAG, "stopBackgroundUsersIfEnforced stopping " + oldUserId + " and related users"); - stopUsersLocked(oldUserId, false, null); + stopUsersLU(oldUserId, false, null); } } - void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) { + private void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) { synchronized (mLock) { Slog.e(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId); mTimeoutUserSwitchCallbacks = mCurWaitingUserSwitchCallbacks; mHandler.removeMessages(USER_SWITCH_CALLBACKS_TIMEOUT_MSG); - sendContinueUserSwitchLocked(uss, oldUserId, newUserId); + sendContinueUserSwitchLU(uss, oldUserId, newUserId); // Report observers that never called back (USER_SWITCH_CALLBACKS_TIMEOUT) mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_CALLBACKS_TIMEOUT_MSG, oldUserId, newUserId), USER_SWITCH_CALLBACKS_TIMEOUT_MS); } } - void timeoutUserSwitchCallbacks(int oldUserId, int newUserId) { + private void timeoutUserSwitchCallbacks(int oldUserId, int newUserId) { synchronized (mLock) { if (mTimeoutUserSwitchCallbacks != null && !mTimeoutUserSwitchCallbacks.isEmpty()) { Slog.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId @@ -1261,7 +1272,7 @@ class UserController implements Handler.Callback { if (waitingCallbacksCount.decrementAndGet() == 0 && (curWaitingUserSwitchCallbacks == mCurWaitingUserSwitchCallbacks)) { - sendContinueUserSwitchLocked(uss, oldUserId, newUserId); + sendContinueUserSwitchLU(uss, oldUserId, newUserId); } } } @@ -1272,13 +1283,13 @@ class UserController implements Handler.Callback { } } else { synchronized (mLock) { - sendContinueUserSwitchLocked(uss, oldUserId, newUserId); + sendContinueUserSwitchLU(uss, oldUserId, newUserId); } } mUserSwitchObservers.finishBroadcast(); } - void sendContinueUserSwitchLocked(UserState uss, int oldUserId, int newUserId) { + void sendContinueUserSwitchLU(UserState uss, int oldUserId, int newUserId) { mCurWaitingUserSwitchCallbacks = null; mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG); mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG, @@ -1288,9 +1299,7 @@ class UserController implements Handler.Callback { void continueUserSwitch(UserState uss, int oldUserId, int newUserId) { Slog.d(TAG, "Continue user switch oldUser #" + oldUserId + ", newUser #" + newUserId); if (mUserSwitchUiEnabled) { - synchronized (mLock) { - mInjector.getWindowManager().stopFreezingScreen(); - } + mInjector.getWindowManager().stopFreezingScreen(); } uss.switching = false; mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG); @@ -1300,19 +1309,18 @@ class UserController implements Handler.Callback { stopBackgroundUsersIfEnforced(oldUserId); } - void moveUserToForegroundLocked(UserState uss, int oldUserId, int newUserId) { - boolean homeInFront = - mInjector.getActivityStackSupervisor().switchUserLocked(newUserId, uss); + private void moveUserToForeground(UserState uss, int oldUserId, int newUserId) { + boolean homeInFront = mInjector.stackSupervisorSwitchUser(newUserId, uss); if (homeInFront) { - mInjector.startHomeActivityLocked(newUserId, "moveUserToForeground"); + mInjector.startHomeActivity(newUserId, "moveUserToForeground"); } else { - mInjector.getActivityStackSupervisor().resumeFocusedStackTopActivityLocked(); + mInjector.stackSupervisorResumeFocusedStackTopActivity(); } EventLogTags.writeAmSwitchUser(newUserId); - sendUserSwitchBroadcastsLocked(oldUserId, newUserId); + sendUserSwitchBroadcasts(oldUserId, newUserId); } - void sendUserSwitchBroadcastsLocked(int oldUserId, int newUserId) { + void sendUserSwitchBroadcasts(int oldUserId, int newUserId) { long ident = Binder.clearCallingIdentity(); try { Intent intent; @@ -1326,7 +1334,7 @@ class UserController implements Handler.Callback { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId); - mInjector.broadcastIntentLocked(intent, + mInjector.broadcastIntent(intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, profileUserId); } @@ -1341,7 +1349,7 @@ class UserController implements Handler.Callback { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId); - mInjector.broadcastIntentLocked(intent, + mInjector.broadcastIntent(intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, profileUserId); } @@ -1349,7 +1357,7 @@ class UserController implements Handler.Callback { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId); - mInjector.broadcastIntentLocked(intent, + mInjector.broadcastIntent(intent, null, null, 0, null, null, new String[] {android.Manifest.permission.MANAGE_USERS}, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, @@ -1374,7 +1382,7 @@ class UserController implements Handler.Callback { // the value the caller will receive and someone else changing it. // We assume that USER_CURRENT_OR_SELF will use the current user; later // we will switch to the calling user if access to the current user fails. - int targetUserId = unsafeConvertIncomingUserLocked(userId); + int targetUserId = unsafeConvertIncomingUser(userId); if (callingUid != 0 && callingUid != SYSTEM_UID) { final boolean allow; @@ -1442,9 +1450,9 @@ class UserController implements Handler.Callback { return targetUserId; } - int unsafeConvertIncomingUserLocked(int userId) { + int unsafeConvertIncomingUser(int userId) { return (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) - ? getCurrentUserIdLocked(): userId; + ? getCurrentUserId(): userId; } void registerUserSwitchObserver(IUserSwitchObserver observer, String name) { @@ -1470,15 +1478,17 @@ class UserController implements Handler.Callback { mUserSwitchObservers.unregister(observer); } - UserState getStartedUserStateLocked(int userId) { - return mStartedUsers.get(userId); + UserState getStartedUserState(int userId) { + synchronized (mLock) { + return mStartedUsers.get(userId); + } } boolean hasStartedUserState(int userId) { return mStartedUsers.get(userId) != null; } - private void updateStartedUserArrayLocked() { + private void updateStartedUserArrayLU() { int num = 0; for (int i = 0; i < mStartedUsers.size(); i++) { UserState uss = mStartedUsers.valueAt(i); @@ -1499,15 +1509,20 @@ class UserController implements Handler.Callback { } } - void sendBootCompletedLocked(IIntentReceiver resultTo) { - for (int i = 0; i < mStartedUsers.size(); i++) { - UserState uss = mStartedUsers.valueAt(i); + void sendBootCompleted(IIntentReceiver resultTo) { + // Get a copy of mStartedUsers to use outside of lock + SparseArray<UserState> startedUsers; + synchronized (mLock) { + startedUsers = mStartedUsers.clone(); + } + for (int i = 0; i < startedUsers.size(); i++) { + UserState uss = startedUsers.valueAt(i); finishUserBoot(uss, resultTo); } } void onSystemReady() { - updateCurrentProfileIdsLocked(); + updateCurrentProfileIds(); } /** @@ -1515,33 +1530,35 @@ class UserController implements Handler.Callback { * user switch happens or when a new related user is started in the * background. */ - private void updateCurrentProfileIdsLocked() { - final List<UserInfo> profiles = mInjector.getUserManager().getProfiles(mCurrentUserId, + private void updateCurrentProfileIds() { + final List<UserInfo> profiles = mInjector.getUserManager().getProfiles(getCurrentUserId(), false /* enabledOnly */); int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null for (int i = 0; i < currentProfileIds.length; i++) { currentProfileIds[i] = profiles.get(i).id; } - mCurrentProfileIds = currentProfileIds; + final List<UserInfo> users = mInjector.getUserManager().getUsers(false); + synchronized (mLock) { + mCurrentProfileIds = currentProfileIds; - synchronized (mUserProfileGroupIdsSelfLocked) { - mUserProfileGroupIdsSelfLocked.clear(); - final List<UserInfo> users = mInjector.getUserManager().getUsers(false); + mUserProfileGroupIds.clear(); for (int i = 0; i < users.size(); i++) { UserInfo user = users.get(i); if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) { - mUserProfileGroupIdsSelfLocked.put(user.id, user.profileGroupId); + mUserProfileGroupIds.put(user.id, user.profileGroupId); } } } } - int[] getStartedUserArrayLocked() { - return mStartedUserArray; + int[] getStartedUserArray() { + synchronized (mLock) { + return mStartedUserArray; + } } - boolean isUserRunningLocked(int userId, int flags) { - UserState state = getStartedUserStateLocked(userId); + boolean isUserRunning(int userId, int flags) { + UserState state = getStartedUserState(userId); if (state == null) { return false; } @@ -1604,29 +1621,38 @@ class UserController implements Handler.Callback { return getUserInfo(mCurrentUserId); } synchronized (mLock) { - return getCurrentUserLocked(); + return getCurrentUserLU(); } } - UserInfo getCurrentUserLocked() { + UserInfo getCurrentUserLU() { int userId = mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId; return getUserInfo(userId); } - int getCurrentOrTargetUserIdLocked() { + int getCurrentOrTargetUserId() { + synchronized (mLock) { + return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId; + } + } + + int getCurrentOrTargetUserIdLU() { return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId; } - int getCurrentUserIdLocked() { + + int getCurrentUserIdLU() { return mCurrentUserId; } - private boolean isCurrentUserLocked(int userId) { - return userId == getCurrentOrTargetUserIdLocked(); + int getCurrentUserId() { + synchronized (mLock) { + return mCurrentUserId; + } } - int setTargetUserIdLocked(int targetUserId) { - return mTargetUserId = targetUserId; + private boolean isCurrentUserLU(int userId) { + return userId == getCurrentOrTargetUserIdLU(); } int[] getUsers() { @@ -1673,22 +1699,26 @@ class UserController implements Handler.Callback { if (callingUserId == targetUserId) { return true; } - synchronized (mUserProfileGroupIdsSelfLocked) { - int callingProfile = mUserProfileGroupIdsSelfLocked.get(callingUserId, + synchronized (mLock) { + int callingProfile = mUserProfileGroupIds.get(callingUserId, UserInfo.NO_PROFILE_GROUP_ID); - int targetProfile = mUserProfileGroupIdsSelfLocked.get(targetUserId, + int targetProfile = mUserProfileGroupIds.get(targetUserId, UserInfo.NO_PROFILE_GROUP_ID); return callingProfile != UserInfo.NO_PROFILE_GROUP_ID && callingProfile == targetProfile; } } - boolean isCurrentProfileLocked(int userId) { - return ArrayUtils.contains(mCurrentProfileIds, userId); + boolean isCurrentProfile(int userId) { + synchronized (mLock) { + return ArrayUtils.contains(mCurrentProfileIds, userId); + } } - int[] getCurrentProfileIdsLocked() { - return mCurrentProfileIds; + int[] getCurrentProfileIds() { + synchronized (mLock) { + return mCurrentProfileIds; + } } /** @@ -1713,35 +1743,40 @@ class UserController implements Handler.Callback { } void dump(PrintWriter pw, boolean dumpAll) { - pw.println(" mStartedUsers:"); - for (int i = 0; i < mStartedUsers.size(); i++) { - UserState uss = mStartedUsers.valueAt(i); - pw.print(" User #"); pw.print(uss.mHandle.getIdentifier()); - pw.print(": "); uss.dump("", pw); - } - pw.print(" mStartedUserArray: ["); - for (int i = 0; i < mStartedUserArray.length; i++) { - if (i > 0) pw.print(", "); - pw.print(mStartedUserArray[i]); - } - pw.println("]"); - pw.print(" mUserLru: ["); - for (int i = 0; i < mUserLru.size(); i++) { - if (i > 0) pw.print(", "); - pw.print(mUserLru.get(i)); - } - pw.println("]"); - if (dumpAll) { - pw.print(" mStartedUserArray: "); pw.println(Arrays.toString(mStartedUserArray)); - } - synchronized (mUserProfileGroupIdsSelfLocked) { - if (mUserProfileGroupIdsSelfLocked.size() > 0) { + synchronized (mLock) { + pw.println(" mStartedUsers:"); + for (int i = 0; i < mStartedUsers.size(); i++) { + UserState uss = mStartedUsers.valueAt(i); + pw.print(" User #"); + pw.print(uss.mHandle.getIdentifier()); + pw.print(": "); + uss.dump("", pw); + } + pw.print(" mStartedUserArray: ["); + for (int i = 0; i < mStartedUserArray.length; i++) { + if (i > 0) + pw.print(", "); + pw.print(mStartedUserArray[i]); + } + pw.println("]"); + pw.print(" mUserLru: ["); + for (int i = 0; i < mUserLru.size(); i++) { + if (i > 0) + pw.print(", "); + pw.print(mUserLru.get(i)); + } + pw.println("]"); + if (dumpAll) { + pw.print(" mStartedUserArray: "); + pw.println(Arrays.toString(mStartedUserArray)); + } + if (mUserProfileGroupIds.size() > 0) { pw.println(" mUserProfileGroupIds:"); - for (int i=0; i<mUserProfileGroupIdsSelfLocked.size(); i++) { + for (int i=0; i< mUserProfileGroupIds.size(); i++) { pw.print(" User #"); - pw.print(mUserProfileGroupIdsSelfLocked.keyAt(i)); + pw.print(mUserProfileGroupIds.keyAt(i)); pw.print(" -> profile #"); - pw.println(mUserProfileGroupIdsSelfLocked.valueAt(i)); + pw.println(mUserProfileGroupIds.valueAt(i)); } } } @@ -1765,9 +1800,7 @@ class UserController implements Handler.Callback { timeoutUserSwitchCallbacks(msg.arg1, msg.arg2); break; case START_PROFILES_MSG: - synchronized (mLock) { - startProfilesLocked(); - } + startProfiles(); break; case SYSTEM_USER_START_MSG: mInjector.batteryStatsServiceNoteEvent( @@ -1778,9 +1811,7 @@ class UserController implements Handler.Callback { case SYSTEM_USER_UNLOCK_MSG: final int userId = msg.arg1; mInjector.getSystemServiceManager().unlockUser(userId); - synchronized (mLock) { - mInjector.loadUserRecentsLocked(userId); - } + mInjector.loadUserRecents(userId); if (userId == UserHandle.USER_SYSTEM) { mInjector.startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_UNAWARE); } @@ -1823,10 +1854,6 @@ class UserController implements Handler.Callback { mService = service; } - protected Object getLock() { - return mService; - } - protected Handler getHandler(Handler.Callback callback) { return new Handler(mService.mHandlerThread.getLooper(), callback); } @@ -1843,13 +1870,16 @@ class UserController implements Handler.Callback { return new LockPatternUtils(getContext()); } - protected int broadcastIntentLocked(Intent intent, String resolvedType, + protected int broadcastIntent(Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) { - return mService.broadcastIntentLocked(null, null, intent, resolvedType, resultTo, - resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, - ordered, sticky, callingPid, callingUid, userId); + // TODO b/64165549 Verify that mLock is not held before calling AMS methods + synchronized (mService) { + return mService.broadcastIntentLocked(null, null, intent, resolvedType, resultTo, + resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, + ordered, sticky, callingPid, callingUid, userId); + } } int checkCallingPermission(String permission) { @@ -1860,7 +1890,9 @@ class UserController implements Handler.Callback { return mService.mWindowManager; } void activityManagerOnUserStopped(int userId) { - mService.onUserStoppedLocked(userId); + synchronized (mService) { + mService.onUserStoppedLocked(userId); + } } void systemServiceManagerCleanupUser(int userId) { @@ -1916,9 +1948,11 @@ class UserController implements Handler.Callback { }.sendNext(); } - void activityManagerForceStopPackageLocked(int userId, String reason) { - mService.forceStopPackageLocked(null, -1, false, false, true, false, false, - userId, reason); + void activityManagerForceStopPackage(int userId, String reason) { + synchronized (mService) { + mService.forceStopPackageLocked(null, -1, false, false, true, false, false, + userId, reason); + } }; int checkComponentPermission(String permission, int pid, int uid, int owningUid, @@ -1926,20 +1960,28 @@ class UserController implements Handler.Callback { return mService.checkComponentPermission(permission, pid, uid, owningUid, exported); } - void startHomeActivityLocked(int userId, String reason) { - mService.startHomeActivityLocked(userId, reason); + protected void startHomeActivity(int userId, String reason) { + synchronized (mService) { + mService.startHomeActivityLocked(userId, reason); + } } - void updateUserConfigurationLocked() { - mService.updateUserConfigurationLocked(); + void updateUserConfiguration() { + synchronized (mService) { + mService.updateUserConfigurationLocked(); + } } - void clearBroadcastQueueForUserLocked(int userId) { - mService.clearBroadcastQueueForUserLocked(userId); + void clearBroadcastQueueForUser(int userId) { + synchronized (mService) { + mService.clearBroadcastQueueForUserLocked(userId); + } } - void loadUserRecentsLocked(int userId) { - mService.mRecentTasks.loadUserRecentsLocked(userId); + void loadUserRecents(int userId) { + synchronized (mService) { + mService.mRecentTasks.loadUserRecentsLocked(userId); + } } void startPersistentApps(int matchFlags) { @@ -1956,12 +1998,28 @@ class UserController implements Handler.Callback { d.show(); } - ActivityStackSupervisor getActivityStackSupervisor() { - return mService.mStackSupervisor; + void stackSupervisorRemoveUser(int userId) { + synchronized (mService) { + mService.mStackSupervisor.removeUserLocked(userId); + } } - LockTaskController getLockTaskController() { - return mService.mLockTaskController; + protected boolean stackSupervisorSwitchUser(int userId, UserState uss) { + synchronized (mService) { + return mService.mStackSupervisor.switchUserLocked(userId, uss); + } + } + + protected void stackSupervisorResumeFocusedStackTopActivity() { + synchronized (mService) { + mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); + } + } + + protected void clearLockTaskMode(String reason) { + synchronized (mService) { + mService.mLockTaskController.clearLockTaskMode(reason); + } } } } diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java index 2e27387a6a09..d36d9cbef930 100644 --- a/services/core/java/com/android/server/am/UserState.java +++ b/services/core/java/com/android/server/am/UserState.java @@ -59,8 +59,10 @@ public final class UserState { /** * The last time that a provider was reported to usage stats as being brought to important * foreground procstate. + * <p><strong>Important: </strong>Only access this field when holding ActivityManagerService + * lock. */ - public final ArrayMap<String,Long> mProviderLastReportedFg = new ArrayMap<>(); + final ArrayMap<String,Long> mProviderLastReportedFg = new ArrayMap<>(); public UserState(UserHandle handle) { mHandle = handle; diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index 419a161669aa..d6d0209bca7d 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -30,6 +30,7 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.UserManagerInternal; +import android.platform.test.annotations.Presubmit; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; @@ -59,14 +60,20 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; +/** + * Usage: bit FrameworksServicesTests:com.android.server.am.UserControllerTest + */ +@Presubmit public class UserControllerTest extends AndroidTestCase { private static final int TEST_USER_ID = 10; private static final int NONEXIST_USER_ID = 2; @@ -96,8 +103,11 @@ public class UserControllerTest extends AndroidTestCase { @Override public void setUp() throws Exception { super.setUp(); - System.setProperty("dexmaker.share_classloader", "true"); - mInjector = new TestInjector(getContext()); + mInjector = Mockito.spy(new TestInjector(getContext())); + doNothing().when(mInjector).clearLockTaskMode(anyString()); + doNothing().when(mInjector).startHomeActivity(anyInt(), anyString()); + doReturn(false).when(mInjector).stackSupervisorSwitchUser(anyInt(), any()); + doNothing().when(mInjector).stackSupervisorResumeFocusedStackTopActivity(); mUserController = new UserController(mInjector); setUpUser(TEST_USER_ID, 0); } @@ -106,6 +116,7 @@ public class UserControllerTest extends AndroidTestCase { protected void tearDown() throws Exception { super.tearDown(); mInjector.handlerThread.quit(); + Mockito.validateMockitoUsage(); } @SmallTest @@ -115,7 +126,7 @@ public class UserControllerTest extends AndroidTestCase { Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen(); Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean()); Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(true); - Mockito.verify(mInjector.getLockTaskController()).clearLockTaskMode(anyString()); + Mockito.verify(mInjector).clearLockTaskMode(anyString()); startForegroundUserAssertions(); } @@ -125,7 +136,7 @@ public class UserControllerTest extends AndroidTestCase { Mockito.verify( mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt()); Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean()); - verifyZeroInteractions(mInjector.getLockTaskController()); + Mockito.verify(mInjector, never()).clearLockTaskMode(anyString()); startBackgroundUserAssertions(); } @@ -306,16 +317,14 @@ public class UserControllerTest extends AndroidTestCase { return result; } - private static class TestInjector extends UserController.Injector { - final Object lock = new Object(); + // Should be public to allow mocking + public static class TestInjector extends UserController.Injector { TestHandler handler; TestHandler uiHandler; HandlerThread handlerThread; UserManagerService userManagerMock; UserManagerInternal userManagerInternalMock; WindowManagerService windowManagerMock; - ActivityStackSupervisor activityStackSupervisor; - LockTaskController lockTaskController; private Context mCtx; List<Intent> sentIntents = new ArrayList<>(); @@ -329,13 +338,6 @@ public class UserControllerTest extends AndroidTestCase { userManagerMock = mock(UserManagerService.class); userManagerInternalMock = mock(UserManagerInternal.class); windowManagerMock = mock(WindowManagerService.class); - activityStackSupervisor = mock(ActivityStackSupervisor.class); - lockTaskController = mock(LockTaskController.class); - } - - @Override - protected Object getLock() { - return lock; } @Override @@ -375,12 +377,12 @@ public class UserControllerTest extends AndroidTestCase { } @Override - void updateUserConfigurationLocked() { - Log.i(TAG, "updateUserConfigurationLocked"); + void updateUserConfiguration() { + Log.i(TAG, "updateUserConfiguration"); } @Override - protected int broadcastIntentLocked(Intent intent, String resolvedType, + protected int broadcastIntent(Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) { @@ -388,20 +390,6 @@ public class UserControllerTest extends AndroidTestCase { sentIntents.add(intent); return 0; } - - @Override - void startHomeActivityLocked(int userId, String reason) { - Log.i(TAG, "startHomeActivityLocked " + userId); - } - - @Override - ActivityStackSupervisor getActivityStackSupervisor() { - return activityStackSupervisor; - } - - LockTaskController getLockTaskController() { - return lockTaskController; - } } private static class TestHandler extends Handler { |