Delay user unlocking until boot complete on HSUM.
- When user 0 and user 10+ are unlocked early in the boot process on devices that use headless system user mode (HSUM), vold creates and mounts emulated volumes for these users before boot completed. On boot complete, vold is reset by StorageManagerService, causing vold to unmount previously mounted emulated volumes. This leads to vold killing apps/services that use these volumes. On some occasions, these killings have caused the system to become unstable and crash, or some apps to crash. This creates bad user experience on HSUM. - In order to avoid this issue, delay user unlocking until boot complete on HSUM. Thus, emulated volume creation + mount and vold reset will happen after boot complete. - To mitigate the risk of this CL impacting non-automotive builds, it is explicitly checking for HSUM in a way that minimize the code changes; a more robust solution be provided on master (commit 3d3a13419fd34ae460fffd68de97c13bc47295d7will). Test: atest --rerun-until-failure 10 AtsCarWatchdogHostTests on tm-qpr-dev build id 8877634. AtsCarWatchdogHostTests has high failure rate on this build. So, tested the fix on this build to ensure the fix works. Test: atest UserControllerTest Bug: 240112925 Bug: 241929666 Change-Id: I2c1f2db625177511eaeeba56fba9d500d16b92d7
This commit is contained in:
committed by
Felipe Leme
parent
f28ee2053d
commit
5f5c13eda3
@@ -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;
|
||||
@@ -367,6 +368,11 @@ class UserController implements Handler.Callback {
|
||||
@GuardedBy("mLock")
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
@@ -599,6 +604,16 @@ public class UserControllerTest {
|
||||
/* keyEvictedCallback= */ mKeyEvictedCallback, /* expectLocking= */ true);
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
Reference in New Issue
Block a user