Loading services/core/java/com/android/server/am/UserController.java +26 −3 Original line number Diff line number Diff line Loading @@ -126,6 +126,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.ObjectUtils; import com.android.internal.util.Preconditions; import com.android.internal.widget.LockPatternUtils; import com.android.server.FactoryResetter; Loading @@ -146,6 +147,7 @@ import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerService; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; Loading Loading @@ -259,6 +261,10 @@ class UserController implements Handler.Callback { // once target user goes into the foreground. Use mLock when updating @GuardedBy("mLock") private volatile int mTargetUserId = UserHandle.USER_NULL; // If a user switch request comes during an ongoing user switch, it is postponed to the end of // the current switch, and this variable holds those user ids. Use mLock when updating @GuardedBy("mLock") private final ArrayDeque<Integer> mPendingTargetUserIds = new ArrayDeque<>(); /** * Which users have been started, so are allowed to run code. Loading Loading @@ -1691,7 +1697,6 @@ class UserController implements Handler.Callback { boolean userSwitchUiEnabled; synchronized (mLock) { mCurrentUserId = userId; mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up userSwitchUiEnabled = mUserSwitchUiEnabled; } mInjector.updateUserConfiguration(); Loading Loading @@ -1819,8 +1824,7 @@ class UserController implements Handler.Callback { boolean success = startUser(targetUserId, USER_START_MODE_FOREGROUND); if (!success) { mInjector.getWindowManager().setSwitchingUser(false); mTargetUserId = UserHandle.USER_NULL; dismissUserSwitchDialog(null); dismissUserSwitchDialog(this::endUserSwitch); } } Loading Loading @@ -1948,6 +1952,12 @@ class UserController implements Handler.Callback { + ": UserController not ready yet"); return false; } if (mTargetUserId != UserHandle.USER_NULL) { Slogf.w(TAG, "There is already an ongoing user switch to User #" + mTargetUserId + ". User #" + targetUserId + " will be added to the queue."); mPendingTargetUserIds.offer(targetUserId); return true; } mTargetUserId = targetUserId; userSwitchUiEnabled = mUserSwitchUiEnabled; } Loading Loading @@ -2014,6 +2024,19 @@ class UserController implements Handler.Callback { sendUserSwitchBroadcasts(oldUserId, newUserId); t.traceEnd(); t.traceEnd(); endUserSwitch(); } private void endUserSwitch() { final int nextUserId; synchronized (mLock) { nextUserId = ObjectUtils.getOrElse(mPendingTargetUserIds.poll(), UserHandle.USER_NULL); mTargetUserId = UserHandle.USER_NULL; } if (nextUserId != UserHandle.USER_NULL) { switchUser(nextUserId); } } private void dispatchLockedBootComplete(@UserIdInt int userId) { Loading services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +18 −0 Original line number Diff line number Diff line Loading @@ -1276,6 +1276,24 @@ public final class UserManagerTest { assertThrows(IllegalArgumentException.class, () -> mActivityManager.switchUser(null)); } @MediumTest @Test public void testConcurrentUserSwitch() { final int startUser = ActivityManager.getCurrentUser(); final UserInfo user1 = createUser("User 1", 0); assertThat(user1).isNotNull(); final UserInfo user2 = createUser("User 2", 0); assertThat(user2).isNotNull(); final UserInfo user3 = createUser("User 3", 0); assertThat(user3).isNotNull(); // Switch to the users just created without waiting for the completion of the previous one. switchUserThenRun(user1.id, () -> switchUserThenRun(user2.id, () -> switchUser(user3.id))); // Switch back to the starting user. switchUser(startUser); } @MediumTest @Test public void testConcurrentUserCreate() throws Exception { Loading Loading
services/core/java/com/android/server/am/UserController.java +26 −3 Original line number Diff line number Diff line Loading @@ -126,6 +126,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.ObjectUtils; import com.android.internal.util.Preconditions; import com.android.internal.widget.LockPatternUtils; import com.android.server.FactoryResetter; Loading @@ -146,6 +147,7 @@ import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerService; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; Loading Loading @@ -259,6 +261,10 @@ class UserController implements Handler.Callback { // once target user goes into the foreground. Use mLock when updating @GuardedBy("mLock") private volatile int mTargetUserId = UserHandle.USER_NULL; // If a user switch request comes during an ongoing user switch, it is postponed to the end of // the current switch, and this variable holds those user ids. Use mLock when updating @GuardedBy("mLock") private final ArrayDeque<Integer> mPendingTargetUserIds = new ArrayDeque<>(); /** * Which users have been started, so are allowed to run code. Loading Loading @@ -1691,7 +1697,6 @@ class UserController implements Handler.Callback { boolean userSwitchUiEnabled; synchronized (mLock) { mCurrentUserId = userId; mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up userSwitchUiEnabled = mUserSwitchUiEnabled; } mInjector.updateUserConfiguration(); Loading Loading @@ -1819,8 +1824,7 @@ class UserController implements Handler.Callback { boolean success = startUser(targetUserId, USER_START_MODE_FOREGROUND); if (!success) { mInjector.getWindowManager().setSwitchingUser(false); mTargetUserId = UserHandle.USER_NULL; dismissUserSwitchDialog(null); dismissUserSwitchDialog(this::endUserSwitch); } } Loading Loading @@ -1948,6 +1952,12 @@ class UserController implements Handler.Callback { + ": UserController not ready yet"); return false; } if (mTargetUserId != UserHandle.USER_NULL) { Slogf.w(TAG, "There is already an ongoing user switch to User #" + mTargetUserId + ". User #" + targetUserId + " will be added to the queue."); mPendingTargetUserIds.offer(targetUserId); return true; } mTargetUserId = targetUserId; userSwitchUiEnabled = mUserSwitchUiEnabled; } Loading Loading @@ -2014,6 +2024,19 @@ class UserController implements Handler.Callback { sendUserSwitchBroadcasts(oldUserId, newUserId); t.traceEnd(); t.traceEnd(); endUserSwitch(); } private void endUserSwitch() { final int nextUserId; synchronized (mLock) { nextUserId = ObjectUtils.getOrElse(mPendingTargetUserIds.poll(), UserHandle.USER_NULL); mTargetUserId = UserHandle.USER_NULL; } if (nextUserId != UserHandle.USER_NULL) { switchUser(nextUserId); } } private void dispatchLockedBootComplete(@UserIdInt int userId) { Loading
services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +18 −0 Original line number Diff line number Diff line Loading @@ -1276,6 +1276,24 @@ public final class UserManagerTest { assertThrows(IllegalArgumentException.class, () -> mActivityManager.switchUser(null)); } @MediumTest @Test public void testConcurrentUserSwitch() { final int startUser = ActivityManager.getCurrentUser(); final UserInfo user1 = createUser("User 1", 0); assertThat(user1).isNotNull(); final UserInfo user2 = createUser("User 2", 0); assertThat(user2).isNotNull(); final UserInfo user3 = createUser("User 3", 0); assertThat(user3).isNotNull(); // Switch to the users just created without waiting for the completion of the previous one. switchUserThenRun(user1.id, () -> switchUserThenRun(user2.id, () -> switchUser(user3.id))); // Switch back to the starting user. switchUser(startUser); } @MediumTest @Test public void testConcurrentUserCreate() throws Exception { Loading