diff options
| -rw-r--r-- | services/core/java/com/android/server/am/UserController.java | 65 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/am/UserControllerTest.java | 15 |
2 files changed, 80 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 7ffea26638f5..44b186e1541f 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -106,6 +106,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.Preconditions; import com.android.internal.widget.LockPatternUtils; import com.android.server.FactoryResetter; import com.android.server.FgThread; @@ -368,6 +369,11 @@ class UserController implements Handler.Callback { private boolean mDelayUserDataLocking; /** + * Users are only allowed to be unlocked after boot complete. + */ + private volatile boolean mAllowUserUnlocking; + + /** * Keep track of last active users for mDelayUserDataLocking. * The latest stopped user is placed in front while the least recently stopped user in back. */ @@ -426,6 +432,11 @@ class UserController implements Handler.Callback { mUserLru.add(UserHandle.USER_SYSTEM); mLockPatternUtils = mInjector.getLockPatternUtils(); updateStartedUserArrayLU(); + + // TODO(b/232452368): currently mAllowUserUnlocking is only used on devices with HSUM + // (Headless System User Mode), but on master it will be used by all devices (and hence this + // initial assignment should be removed). + mAllowUserUnlocking = !UserManager.isHeadlessSystemUserMode(); } void setInitialConfig(boolean userSwitchUiEnabled, int maxRunningUsers, @@ -1742,6 +1753,22 @@ class UserController implements Handler.Callback { private boolean unlockUserCleared(final @UserIdInt int userId, byte[] secret, IProgressListener listener) { + // Delay user unlocking for headless system user mode until the system boot + // completes. When the system boot completes, the {@link #onBootCompleted()} + // method unlocks all started users for headless system user mode. This is done + // to prevent unlocking the users too early during the system boot up. + // Otherwise, emulated volumes are mounted too early during the system + // boot up. When vold is reset on boot complete, vold kills all apps/services + // (that use these emulated volumes) before unmounting the volumes(b/241929666). + // In the past, these killings have caused the system to become too unstable on + // some occasions. + // Any unlocks that get delayed by this will be done by onBootComplete() instead. + if (!mAllowUserUnlocking) { + Slogf.i(TAG, "Not unlocking user %d yet because boot hasn't completed", userId); + notifyFinished(userId, listener); + return false; + } + UserState uss; if (!StorageManager.isUserKeyUnlocked(userId)) { final UserInfo userInfo = getUserInfo(userId); @@ -2331,7 +2358,44 @@ class UserController implements Handler.Callback { } } + @VisibleForTesting + void setAllowUserUnlocking(boolean allowed) { + mAllowUserUnlocking = allowed; + if (DEBUG_MU) { + // TODO(b/245335748): use Slogf.d instead + // Slogf.d(TAG, new Exception(), "setAllowUserUnlocking(%b)", allowed); + android.util.Slog.d(TAG, "setAllowUserUnlocking():" + allowed, new Exception()); + } + } + + /** + * @deprecated TODO(b/232452368): this logic will be merged into sendBootCompleted + */ + @Deprecated + private void onBootCompletedOnHeadlessSystemUserModeDevices() { + setAllowUserUnlocking(true); + + // Get a copy of mStartedUsers to use outside of lock. + SparseArray<UserState> startedUsers; + synchronized (mLock) { + startedUsers = mStartedUsers.clone(); + } + // USER_SYSTEM must be processed first. It will be first in the array, as its ID is lowest. + Preconditions.checkArgument(startedUsers.keyAt(0) == UserHandle.USER_SYSTEM); + for (int i = 0; i < startedUsers.size(); i++) { + UserState uss = startedUsers.valueAt(i); + int userId = uss.mHandle.getIdentifier(); + Slogf.i(TAG, "Attempting to unlock user %d on boot complete", userId); + maybeUnlockUser(userId); + } + } + void sendBootCompleted(IIntentReceiver resultTo) { + if (UserManager.isHeadlessSystemUserMode()) { + // Unlocking users is delayed until boot complete for headless system user mode. + onBootCompletedOnHeadlessSystemUserModeDevices(); + } + // Get a copy of mStartedUsers to use outside of lock SparseArray<UserState> startedUsers; synchronized (mLock) { @@ -2773,6 +2837,7 @@ class UserController implements Handler.Callback { pw.println(" mTargetUserId:" + mTargetUserId); pw.println(" mLastActiveUsers:" + mLastActiveUsers); pw.println(" mDelayUserDataLocking:" + mDelayUserDataLocking); + pw.println(" mAllowUserUnlocking:" + mAllowUserUnlocking); pw.println(" shouldStopUserOnSwitch():" + shouldStopUserOnSwitch()); pw.println(" mStopUserOnSwitch:" + mStopUserOnSwitch); pw.println(" mMaxRunningUsers:" + mMaxRunningUsers); 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 db1209224bd5..1508055263ef 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -179,6 +179,11 @@ public class UserControllerTest { doNothing().when(mInjector).taskSupervisorRemoveUser(anyInt()); // All UserController params are set to default. mUserController = new UserController(mInjector); + + // TODO(b/232452368): need to explicitly call setAllowUserUnlocking(), otherwise most + // tests would fail. But we might need to disable it for the onBootComplete() test (i.e, + // to make sure the users are unlocked at the right time) + mUserController.setAllowUserUnlocking(true); setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS); setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated=*/ true, null); }); @@ -600,6 +605,16 @@ public class UserControllerTest { } @Test + public void testUserNotUnlockedBeforeAllowed() throws Exception { + mUserController.setAllowUserUnlocking(false); + + mUserController.startUser(TEST_USER_ID, /* foreground= */ false); + + verify(mInjector.mStorageManagerMock, never()) + .unlockUserKey(eq(TEST_USER_ID), anyInt(), any()); + } + + @Test public void testStartProfile_fullUserFails() { setUpUser(TEST_USER_ID1, 0); assertThrows(IllegalArgumentException.class, |