diff options
10 files changed, 244 insertions, 33 deletions
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java index c4012688406d..57749d43eb37 100644 --- a/core/java/android/content/pm/UserProperties.java +++ b/core/java/android/content/pm/UserProperties.java @@ -68,6 +68,8 @@ public final class UserProperties implements Parcelable { "authAlwaysRequiredToDisableQuietMode"; private static final String ATTR_DELETE_APP_WITH_PARENT = "deleteAppWithParent"; private static final String ATTR_ALWAYS_VISIBLE = "alwaysVisible"; + private static final String ATTR_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING = + "allowStoppingUserWithDelayedLocking"; private static final String ATTR_CROSS_PROFILE_CONTENT_SHARING_STRATEGY = "crossProfileContentSharingStrategy"; @@ -89,7 +91,8 @@ public final class UserProperties implements Parcelable { INDEX_SHOW_IN_QUIET_MODE, INDEX_SHOW_IN_SHARING_SURFACES, INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE, - INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY + INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY, + INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING, }) @Retention(RetentionPolicy.SOURCE) private @interface PropertyIndex { @@ -110,6 +113,7 @@ public final class UserProperties implements Parcelable { private static final int INDEX_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE = 13; private static final int INDEX_SHOW_IN_SHARING_SURFACES = 14; private static final int INDEX_CROSS_PROFILE_CONTENT_SHARING_STRATEGY = 15; + private static final int INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING = 16; /** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */ private long mPropertiesPresent = 0; @@ -450,6 +454,7 @@ public final class UserProperties implements Parcelable { setCrossProfileIntentResolutionStrategy(orig.getCrossProfileIntentResolutionStrategy()); setDeleteAppWithParent(orig.getDeleteAppWithParent()); setAlwaysVisible(orig.getAlwaysVisible()); + setAllowStoppingUserWithDelayedLocking(orig.getAllowStoppingUserWithDelayedLocking()); } if (hasManagePermission) { // Add items that require MANAGE_USERS or stronger. @@ -725,6 +730,11 @@ public final class UserProperties implements Parcelable { this.mUpdateCrossProfileIntentFiltersOnOTA = val; setPresent(INDEX_UPDATE_CROSS_PROFILE_INTENT_FILTERS_ON_OTA); } + /** + Indicate if {@link com.android.server.pm.CrossProfileIntentFilter}s need to be updated during + OTA update between user-parent + */ + private boolean mUpdateCrossProfileIntentFiltersOnOTA; /** * Returns whether a profile shares media with its parent user. @@ -786,12 +796,38 @@ public final class UserProperties implements Parcelable { } private boolean mAuthAlwaysRequiredToDisableQuietMode; - /* - Indicate if {@link com.android.server.pm.CrossProfileIntentFilter}s need to be updated during - OTA update between user-parent + /** + * Returns whether a user (usually a profile) is allowed to leave the CE storage unlocked when + * stopped. + * + * <p> Setting this property to true will enable the user's CE storage to remain unlocked when + * the user is stopped using + * {@link com.android.server.am.ActivityManagerService#stopUserWithDelayedLocking(int, + * boolean, IStopUserCallback)}. + * + * <p> When this property is false, delayed locking may still be applicable at a global + * level for all users via the {@code config_multiuserDelayUserDataLocking}. That is, delayed + * locking for a user can happen if either the device configuration is set or if this property + * is set. When both, the config and the property value is false, the user storage is always + * locked when the user is stopped. + * @hide */ - private boolean mUpdateCrossProfileIntentFiltersOnOTA; - + public boolean getAllowStoppingUserWithDelayedLocking() { + if (isPresent(INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING)) { + return mAllowStoppingUserWithDelayedLocking; + } + if (mDefaultProperties != null) { + return mDefaultProperties.mAllowStoppingUserWithDelayedLocking; + } + throw new SecurityException( + "You don't have permission to query allowStoppingUserWithDelayedLocking"); + } + /** @hide */ + public void setAllowStoppingUserWithDelayedLocking(boolean val) { + this.mAllowStoppingUserWithDelayedLocking = val; + setPresent(INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING); + } + private boolean mAllowStoppingUserWithDelayedLocking; /** * Returns the user's {@link CrossProfileIntentFilterAccessControlLevel}. @@ -899,6 +935,8 @@ public final class UserProperties implements Parcelable { + ", mCredentialShareableWithParent=" + isCredentialShareableWithParent() + ", mAuthAlwaysRequiredToDisableQuietMode=" + isAuthAlwaysRequiredToDisableQuietMode() + + ", mAllowStoppingUserWithDelayedLocking=" + + getAllowStoppingUserWithDelayedLocking() + ", mDeleteAppWithParent=" + getDeleteAppWithParent() + ", mAlwaysVisible=" + getAlwaysVisible() + ", mCrossProfileContentSharingStrategy=" + getCrossProfileContentSharingStrategy() @@ -929,6 +967,8 @@ public final class UserProperties implements Parcelable { + isCredentialShareableWithParent()); pw.println(prefix + " mAuthAlwaysRequiredToDisableQuietMode=" + isAuthAlwaysRequiredToDisableQuietMode()); + pw.println(prefix + " mAllowStoppingUserWithDelayedLocking=" + + getAllowStoppingUserWithDelayedLocking()); pw.println(prefix + " mDeleteAppWithParent=" + getDeleteAppWithParent()); pw.println(prefix + " mAlwaysVisible=" + getAlwaysVisible()); pw.println(prefix + " mCrossProfileContentSharingStrategy=" @@ -1005,6 +1045,9 @@ public final class UserProperties implements Parcelable { case ATTR_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE: setAuthAlwaysRequiredToDisableQuietMode(parser.getAttributeBoolean(i)); break; + case ATTR_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING: + setAllowStoppingUserWithDelayedLocking(parser.getAttributeBoolean(i)); + break; case ATTR_DELETE_APP_WITH_PARENT: setDeleteAppWithParent(parser.getAttributeBoolean(i)); break; @@ -1079,6 +1122,10 @@ public final class UserProperties implements Parcelable { serializer.attributeBoolean(null, ATTR_AUTH_ALWAYS_REQUIRED_TO_DISABLE_QUIET_MODE, mAuthAlwaysRequiredToDisableQuietMode); } + if (isPresent(INDEX_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING)) { + serializer.attributeBoolean(null, ATTR_ALLOW_STOPPING_USER_WITH_DELAYED_LOCKING, + mAllowStoppingUserWithDelayedLocking); + } if (isPresent(INDEX_DELETE_APP_WITH_PARENT)) { serializer.attributeBoolean(null, ATTR_DELETE_APP_WITH_PARENT, mDeleteAppWithParent); @@ -1110,6 +1157,7 @@ public final class UserProperties implements Parcelable { dest.writeBoolean(mMediaSharedWithParent); dest.writeBoolean(mCredentialShareableWithParent); dest.writeBoolean(mAuthAlwaysRequiredToDisableQuietMode); + dest.writeBoolean(mAllowStoppingUserWithDelayedLocking); dest.writeBoolean(mDeleteAppWithParent); dest.writeBoolean(mAlwaysVisible); dest.writeInt(mCrossProfileContentSharingStrategy); @@ -1136,6 +1184,7 @@ public final class UserProperties implements Parcelable { mMediaSharedWithParent = source.readBoolean(); mCredentialShareableWithParent = source.readBoolean(); mAuthAlwaysRequiredToDisableQuietMode = source.readBoolean(); + mAllowStoppingUserWithDelayedLocking = source.readBoolean(); mDeleteAppWithParent = source.readBoolean(); mAlwaysVisible = source.readBoolean(); mCrossProfileContentSharingStrategy = source.readInt(); @@ -1183,6 +1232,7 @@ public final class UserProperties implements Parcelable { private boolean mMediaSharedWithParent = false; private boolean mCredentialShareableWithParent = false; private boolean mAuthAlwaysRequiredToDisableQuietMode = false; + private boolean mAllowStoppingUserWithDelayedLocking = false; private boolean mDeleteAppWithParent = false; private boolean mAlwaysVisible = false; private @CrossProfileContentSharingStrategy int mCrossProfileContentSharingStrategy = @@ -1302,6 +1352,16 @@ public final class UserProperties implements Parcelable { return this; } + /** Sets the value for {@link #mAllowStoppingUserWithDelayedLocking} + * @hide + */ + public Builder setAllowStoppingUserWithDelayedLocking( + boolean allowStoppingUserWithDelayedLocking) { + mAllowStoppingUserWithDelayedLocking = + allowStoppingUserWithDelayedLocking; + return this; + } + /** Sets the value for {@link #mDeleteAppWithParent} * @hide */ @@ -1352,6 +1412,7 @@ public final class UserProperties implements Parcelable { mMediaSharedWithParent, mCredentialShareableWithParent, mAuthAlwaysRequiredToDisableQuietMode, + mAllowStoppingUserWithDelayedLocking, mDeleteAppWithParent, mAlwaysVisible, mCrossProfileContentSharingStrategy); @@ -1372,6 +1433,7 @@ public final class UserProperties implements Parcelable { boolean mediaSharedWithParent, boolean credentialShareableWithParent, boolean authAlwaysRequiredToDisableQuietMode, + boolean allowStoppingUserWithDelayedLocking, boolean deleteAppWithParent, boolean alwaysVisible, @CrossProfileContentSharingStrategy int crossProfileContentSharingStrategy) { @@ -1390,6 +1452,7 @@ public final class UserProperties implements Parcelable { setCredentialShareableWithParent(credentialShareableWithParent); setAuthAlwaysRequiredToDisableQuietMode( authAlwaysRequiredToDisableQuietMode); + setAllowStoppingUserWithDelayedLocking(allowStoppingUserWithDelayedLocking); setDeleteAppWithParent(deleteAppWithParent); setAlwaysVisible(alwaysVisible); setCrossProfileContentSharingStrategy(crossProfileContentSharingStrategy); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 3e533a6ce601..b992e7714617 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -17510,6 +17510,8 @@ public class ActivityManagerService extends IActivityManager.Stub * other {@code ActivityManager#USER_OP_*} codes for failure. * */ + // TODO(b/302662311): Add javadoc changes corresponding to the user property that allows + // delayed locking behavior once the private space flag is finalized. @Override public int stopUserWithDelayedLocking(final int userId, boolean force, final IStopUserCallback callback) { diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 47a99fe24ec4..a6b532cdef09 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -356,6 +356,8 @@ class UserController implements Handler.Callback { * Once total number of unlocked users reach mMaxRunningUsers, least recently used user * will be locked. */ + // TODO(b/302662311): Add javadoc changes corresponding to the user property that allows + // delayed locking behavior once the private space flag is finalized. @GuardedBy("mLock") private boolean mDelayUserDataLocking; @@ -365,11 +367,12 @@ class UserController implements Handler.Callback { 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. + * Keep track of last active users for delayUserDataLocking. + * The most recently stopped user with delayed locking is placed in front, while the least + * recently stopped user in back. */ @GuardedBy("mLock") - private final ArrayList<Integer> mLastActiveUsers = new ArrayList<>(); + private final ArrayList<Integer> mLastActiveUsersForDelayedLocking = new ArrayList<>(); /** * Map of userId to {@link UserCompletedEventType} event flags, indicating which as-yet- @@ -1011,20 +1014,21 @@ class UserController implements Handler.Callback { Slogf.i(TAG, "stopSingleUserLU userId=" + userId); final UserState uss = mStartedUsers.get(userId); if (uss == null) { // User is not started - // If mDelayUserDataLocking is set and allowDelayedLocking is not set, we need to lock - // the requested user as the client wants to stop and lock the user. On the other hand, - // having keyEvictedCallback set will lead into locking user if mDelayUserDataLocking - // is set as that means client wants to lock the user immediately. - // If mDelayUserDataLocking is not set, the user was already locked when it was stopped - // and no further action is necessary. - if (mDelayUserDataLocking) { + // If canDelayDataLockingForUser() is true and allowDelayedLocking is false, we need + // to lock the requested user as the client wants to stop and lock the user. On the + // other hand, having keyEvictedCallback set will lead into locking user if + // canDelayDataLockingForUser() is true as that means client wants to lock the user + // immediately. + // If canDelayDataLockingForUser() is false, the user was already locked when it was + // stopped and no further action is necessary. + if (canDelayDataLockingForUser(userId)) { if (allowDelayedLocking && keyEvictedCallback != null) { Slogf.wtf(TAG, "allowDelayedLocking set with KeyEvictedCallback, ignore it" + " and lock user:" + userId, new RuntimeException()); allowDelayedLocking = false; } if (!allowDelayedLocking) { - if (mLastActiveUsers.remove(Integer.valueOf(userId))) { + if (mLastActiveUsersForDelayedLocking.remove(Integer.valueOf(userId))) { // should lock the user, user is already gone final ArrayList<KeyEvictedCallback> keyEvictedCallbacks; if (keyEvictedCallback != null) { @@ -1354,14 +1358,21 @@ class UserController implements Handler.Callback { @GuardedBy("mLock") private int updateUserToLockLU(@UserIdInt int userId, boolean allowDelayedLocking) { int userIdToLock = userId; - if (mDelayUserDataLocking && allowDelayedLocking && !getUserInfo(userId).isEphemeral() + // TODO: Decouple the delayed locking flows from mMaxRunningUsers or rename the property to + // state maximum running unlocked users specifically + if (canDelayDataLockingForUser(userIdToLock) && allowDelayedLocking + && !getUserInfo(userId).isEphemeral() && !hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND, userId)) { - mLastActiveUsers.remove((Integer) userId); // arg should be object, not index - mLastActiveUsers.add(0, userId); - int totalUnlockedUsers = mStartedUsers.size() + mLastActiveUsers.size(); + // arg should be object, not index + mLastActiveUsersForDelayedLocking.remove((Integer) userId); + mLastActiveUsersForDelayedLocking.add(0, userId); + int totalUnlockedUsers = mStartedUsers.size() + + mLastActiveUsersForDelayedLocking.size(); if (totalUnlockedUsers > mMaxRunningUsers) { // should lock a user - userIdToLock = mLastActiveUsers.get(mLastActiveUsers.size() - 1); - mLastActiveUsers.remove(mLastActiveUsers.size() - 1); + userIdToLock = mLastActiveUsersForDelayedLocking.get( + mLastActiveUsersForDelayedLocking.size() - 1); + mLastActiveUsersForDelayedLocking + .remove(mLastActiveUsersForDelayedLocking.size() - 1); Slogf.i(TAG, "finishUserStopped, stopping user:" + userId + " lock user:" + userIdToLock); } else { @@ -1374,6 +1385,24 @@ class UserController implements Handler.Callback { } /** + * Returns whether the user can have its CE storage left unlocked, even when it is stopped, + * either due to a global device configuration or an individual user's property. + */ + private boolean canDelayDataLockingForUser(@UserIdInt int userIdToLock) { + if (allowBiometricUnlockForPrivateProfile()) { + final UserProperties userProperties = getUserProperties(userIdToLock); + return (mDelayUserDataLocking || (userProperties != null + && userProperties.getAllowStoppingUserWithDelayedLocking())); + } + return mDelayUserDataLocking; + } + + private boolean allowBiometricUnlockForPrivateProfile() { + return android.os.Flags.allowPrivateProfile() + && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace(); + } + + /** * Determines the list of users that should be stopped together with the specified * {@code userId}. The returned list includes {@code userId}. */ @@ -3161,7 +3190,7 @@ class UserController implements Handler.Callback { pw.println(" mCurrentProfileIds:" + Arrays.toString(mCurrentProfileIds)); pw.println(" mCurrentUserId:" + mCurrentUserId); pw.println(" mTargetUserId:" + mTargetUserId); - pw.println(" mLastActiveUsers:" + mLastActiveUsers); + pw.println(" mLastActiveUsersForDelayedLocking:" + mLastActiveUsersForDelayedLocking); pw.println(" mDelayUserDataLocking:" + mDelayUserDataLocking); pw.println(" mAllowUserUnlocking:" + mAllowUserUnlocking); pw.println(" shouldStopUserOnSwitch():" + shouldStopUserOnSwitch()); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index b53a21c9aa1c..a7b52f4e7a58 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1519,7 +1519,7 @@ public class UserManagerService extends IUserManager.Stub { try { if (enableQuietMode) { - ActivityManager.getService().stopUser(userId, /* force= */ true, null); + stopUserForQuietMode(userId); LocalServices.getService(ActivityManagerInternal.class) .killForegroundAppsForUser(userId); } else { @@ -1547,6 +1547,18 @@ public class UserManagerService extends IUserManager.Stub { } } + private void stopUserForQuietMode(int userId) throws RemoteException { + if (android.os.Flags.allowPrivateProfile() + && android.multiuser.Flags.enableBiometricsToUnlockPrivateSpace()) { + // Allow delayed locking since some profile types want to be able to unlock again via + // biometrics. + ActivityManager.getService() + .stopUserWithDelayedLocking(userId, /* force= */ true, null); + return; + } + ActivityManager.getService().stopUser(userId, /* force= */ true, null); + } + private void logQuietModeEnabled(@UserIdInt int userId, boolean enableQuietMode, @Nullable String callingPackage) { Slogf.i(LOG_TAG, diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java index 7f013b8df444..7386301bdd6e 100644 --- a/services/core/java/com/android/server/pm/UserTypeFactory.java +++ b/services/core/java/com/android/server/pm/UserTypeFactory.java @@ -311,6 +311,7 @@ public final class UserTypeFactory { .setStartWithParent(true) .setCredentialShareableWithParent(true) .setAuthAlwaysRequiredToDisableQuietMode(true) + .setAllowStoppingUserWithDelayedLocking(true) .setMediaSharedWithParent(false) .setShowInLauncher(UserProperties.SHOW_IN_LAUNCHER_SEPARATE) .setShowInSettings(UserProperties.SHOW_IN_SETTINGS_SEPARATE) diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml index 9b047f2abff7..6537d47106a9 100644 --- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml +++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml @@ -40,6 +40,7 @@ mediaSharedWithParent='true' credentialShareableWithParent='false' authAlwaysRequiredToDisableQuietMode='true' + allowStoppingUserWithDelayedLocking='true' showInSettings='23' hideInSettingsInQuietMode='true' inheritDevicePolicy='450' 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 d26d67107001..77b1455a2ecc 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -92,6 +92,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.storage.IStorageManager; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import android.util.Log; import android.view.Display; @@ -104,11 +105,14 @@ import com.android.server.am.UserState.KeyEvictedCallback; import com.android.server.pm.UserJourneyLogger; import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerService; +import com.android.server.pm.UserTypeDetails; +import com.android.server.pm.UserTypeFactory; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerService; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -175,6 +179,9 @@ public class UserControllerTest { USER_START_MSG, REPORT_LOCKED_BOOT_COMPLETE_MSG); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Before public void setUp() throws Exception { runWithDexmakerShareClassLoader(() -> { @@ -789,28 +796,99 @@ public class UserControllerTest { } @Test - public void testStartProfile() throws Exception { - setUpAndStartProfileInBackground(TEST_USER_ID1); + public void testStartManagedProfile() throws Exception { + setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_MANAGED); startBackgroundUserAssertions(); verifyUserAssignedToDisplay(TEST_USER_ID1, Display.DEFAULT_DISPLAY); } @Test - public void testStartProfile_whenUsersOnSecondaryDisplaysIsEnabled() throws Exception { + public void testStartManagedProfile_whenUsersOnSecondaryDisplaysIsEnabled() throws Exception { mockIsUsersOnSecondaryDisplaysEnabled(true); - setUpAndStartProfileInBackground(TEST_USER_ID1); + setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_MANAGED); startBackgroundUserAssertions(); verifyUserAssignedToDisplay(TEST_USER_ID1, Display.DEFAULT_DISPLAY); } @Test - public void testStopProfile() throws Exception { - setUpAndStartProfileInBackground(TEST_USER_ID1); + public void testStopManagedProfile() throws Exception { + setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_MANAGED); + assertProfileLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* expectLocking= */ true); + verifyUserUnassignedFromDisplay(TEST_USER_ID1); + } + + @Test + public void testStopPrivateProfile() throws Exception { + mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true, + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, + android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE); + setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_PRIVATE); assertProfileLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* expectLocking= */ true); verifyUserUnassignedFromDisplay(TEST_USER_ID1); + + mSetFlagsRule.disableFlags( + android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE); + setUpAndStartProfileInBackground(TEST_USER_ID2, UserManager.USER_TYPE_PROFILE_PRIVATE); + assertProfileLockedOrUnlockedAfterStopping(TEST_USER_ID2, /* expectLocking= */ true); + verifyUserUnassignedFromDisplay(TEST_USER_ID2); + } + + @Test + public void testStopPrivateProfileWithDelayedLocking() throws Exception { + mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true, + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, + android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE); + setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_PRIVATE); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true, + /* keyEvictedCallback */ null, /* expectLocking= */ false); + } + + @Test + public void testStopPrivateProfileWithDelayedLocking_flagDisabled() throws Exception { + mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true, + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE); + mSetFlagsRule.disableFlags( + android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE); + setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_PRIVATE); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true, + /* keyEvictedCallback */ null, /* expectLocking= */ true); + + mSetFlagsRule.disableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE); + mSetFlagsRule.enableFlags( + android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE); + setUpAndStartProfileInBackground(TEST_USER_ID2, UserManager.USER_TYPE_PROFILE_PRIVATE); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID2, /* delayedLocking= */ true, + /* keyEvictedCallback */ null, /* expectLocking= */ true); + } + + @Test + public void testStopPrivateProfileWithDelayedLocking_maxRunningUsersBreached() + throws Exception { + mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true, + /* maxRunningUsers= */ 1, /* delayUserDataLocking= */ false); + mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, + android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE); + setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_PRIVATE); + setUpAndStartProfileInBackground(TEST_USER_ID2, UserManager.USER_TYPE_PROFILE_MANAGED); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true, + /* keyEvictedCallback */ null, /* expectLocking= */ true); + } + + @Test + public void testStopManagedProfileWithDelayedLocking() throws Exception { + mUserController.setInitialConfig(/* mUserSwitchUiEnabled */ true, + /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false); + mSetFlagsRule.enableFlags(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE, + android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE); + setUpAndStartProfileInBackground(TEST_USER_ID1, UserManager.USER_TYPE_PROFILE_MANAGED); + assertUserLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* delayedLocking= */ true, + /* keyEvictedCallback */ null, /* expectLocking= */ true); } /** Tests handleIncomingUser() for a variety of permissions and situations. */ @@ -1001,8 +1079,8 @@ public class UserControllerTest { mUserStates.put(userId, mUserController.getStartedUserState(userId)); } - private void setUpAndStartProfileInBackground(int userId) throws Exception { - setUpUser(userId, UserInfo.FLAG_PROFILE, false, UserManager.USER_TYPE_PROFILE_MANAGED); + private void setUpAndStartProfileInBackground(int userId, String userType) throws Exception { + setUpUser(userId, UserInfo.FLAG_PROFILE, false, userType); assertThat(mUserController.startProfile(userId, /* evenWhenDisabled=*/ false, /* unlockListener= */ null)).isTrue(); @@ -1070,6 +1148,11 @@ public class UserControllerTest { userInfo.preCreated = preCreated; when(mInjector.mUserManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo); when(mInjector.mUserManagerMock.isPreCreated(userId)).thenReturn(preCreated); + + UserTypeDetails userTypeDetails = UserTypeFactory.getUserTypes().get(userType); + assertThat(userTypeDetails).isNotNull(); + when(mInjector.mUserManagerInternalMock.getUserProperties(eq(userId))) + .thenReturn(userTypeDetails.getDefaultUserPropertiesReference()); } private static List<String> getActions(List<Intent> intents) { diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java index 72cc969b5fb1..d7ed7c2d6469 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java @@ -69,6 +69,7 @@ public class UserManagerServiceUserPropertiesTest { .setMediaSharedWithParent(false) .setCredentialShareableWithParent(true) .setAuthAlwaysRequiredToDisableQuietMode(false) + .setAllowStoppingUserWithDelayedLocking(false) .setDeleteAppWithParent(false) .setAlwaysVisible(false) .setCrossProfileContentSharingStrategy(0) @@ -85,6 +86,7 @@ public class UserManagerServiceUserPropertiesTest { actualProps.setMediaSharedWithParent(true); actualProps.setCredentialShareableWithParent(false); actualProps.setAuthAlwaysRequiredToDisableQuietMode(true); + actualProps.setAllowStoppingUserWithDelayedLocking(true); actualProps.setDeleteAppWithParent(true); actualProps.setAlwaysVisible(true); actualProps.setCrossProfileContentSharingStrategy(1); @@ -130,6 +132,7 @@ public class UserManagerServiceUserPropertiesTest { .setMediaSharedWithParent(true) .setDeleteAppWithParent(true) .setAuthAlwaysRequiredToDisableQuietMode(false) + .setAllowStoppingUserWithDelayedLocking(false) .setAlwaysVisible(true) .build(); final UserProperties orig = new UserProperties(defaultProps); @@ -139,6 +142,7 @@ public class UserManagerServiceUserPropertiesTest { orig.setInheritDevicePolicy(9456); orig.setDeleteAppWithParent(false); orig.setAuthAlwaysRequiredToDisableQuietMode(true); + orig.setAllowStoppingUserWithDelayedLocking(true); orig.setAlwaysVisible(false); // Test every permission level. (Currently, it's linear so it's easy.) @@ -184,6 +188,8 @@ public class UserManagerServiceUserPropertiesTest { assertEqualGetterOrThrows(orig::getDeleteAppWithParent, copy::getDeleteAppWithParent, exposeAll); assertEqualGetterOrThrows(orig::getAlwaysVisible, copy::getAlwaysVisible, exposeAll); + assertEqualGetterOrThrows(orig::getAllowStoppingUserWithDelayedLocking, + copy::getAllowStoppingUserWithDelayedLocking, exposeAll); // Items requiring hasManagePermission - put them here using hasManagePermission. assertEqualGetterOrThrows(orig::getShowInSettings, copy::getShowInSettings, @@ -258,6 +264,8 @@ public class UserManagerServiceUserPropertiesTest { .isEqualTo(actual.isCredentialShareableWithParent()); assertThat(expected.isAuthAlwaysRequiredToDisableQuietMode()) .isEqualTo(actual.isAuthAlwaysRequiredToDisableQuietMode()); + assertThat(expected.getAllowStoppingUserWithDelayedLocking()) + .isEqualTo(actual.getAllowStoppingUserWithDelayedLocking()); assertThat(expected.getDeleteAppWithParent()).isEqualTo(actual.getDeleteAppWithParent()); assertThat(expected.getAlwaysVisible()).isEqualTo(actual.getAlwaysVisible()); assertThat(expected.getCrossProfileContentSharingStrategy()) diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java index d0ad57365942..70837061b0bb 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java @@ -90,6 +90,7 @@ public class UserManagerServiceUserTypeTest { .setMediaSharedWithParent(true) .setCredentialShareableWithParent(false) .setAuthAlwaysRequiredToDisableQuietMode(true) + .setAllowStoppingUserWithDelayedLocking(true) .setShowInSettings(900) .setShowInSharingSurfaces(20) .setShowInQuietMode(30) @@ -167,6 +168,8 @@ public class UserManagerServiceUserTypeTest { assertFalse(type.getDefaultUserPropertiesReference().isCredentialShareableWithParent()); assertTrue(type.getDefaultUserPropertiesReference() .isAuthAlwaysRequiredToDisableQuietMode()); + assertTrue(type.getDefaultUserPropertiesReference() + .getAllowStoppingUserWithDelayedLocking()); assertEquals(900, type.getDefaultUserPropertiesReference().getShowInSettings()); assertEquals(20, type.getDefaultUserPropertiesReference().getShowInSharingSurfaces()); assertEquals(30, @@ -322,6 +325,7 @@ public class UserManagerServiceUserTypeTest { .setMediaSharedWithParent(false) .setCredentialShareableWithParent(true) .setAuthAlwaysRequiredToDisableQuietMode(false) + .setAllowStoppingUserWithDelayedLocking(false) .setShowInSettings(20) .setInheritDevicePolicy(21) .setShowInSharingSurfaces(22) @@ -367,6 +371,8 @@ public class UserManagerServiceUserTypeTest { .isCredentialShareableWithParent()); assertFalse(aospType.getDefaultUserPropertiesReference() .isAuthAlwaysRequiredToDisableQuietMode()); + assertFalse(aospType.getDefaultUserPropertiesReference() + .getAllowStoppingUserWithDelayedLocking()); assertEquals(20, aospType.getDefaultUserPropertiesReference().getShowInSettings()); assertEquals(21, aospType.getDefaultUserPropertiesReference() .getInheritDevicePolicy()); @@ -420,6 +426,8 @@ public class UserManagerServiceUserTypeTest { .isCredentialShareableWithParent()); assertTrue(aospType.getDefaultUserPropertiesReference() .isAuthAlwaysRequiredToDisableQuietMode()); + assertTrue(aospType.getDefaultUserPropertiesReference() + .getAllowStoppingUserWithDelayedLocking()); assertEquals(23, aospType.getDefaultUserPropertiesReference().getShowInSettings()); assertEquals(22, aospType.getDefaultUserPropertiesReference().getShowInSharingSurfaces()); diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java index ced0bb5bc51c..a743fff5d2ea 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java @@ -342,8 +342,12 @@ public final class UserManagerTest { assertThat(typeProps.getCrossProfileContentSharingStrategy()) .isEqualTo(privateProfileUserProperties.getCrossProfileContentSharingStrategy()); assertThrows(SecurityException.class, privateProfileUserProperties::getDeleteAppWithParent); + assertThrows(SecurityException.class, + privateProfileUserProperties::getAllowStoppingUserWithDelayedLocking); + compareDrawables(mUserManager.getUserBadge(), Resources.getSystem().getDrawable(userTypeDetails.getBadgePlain())); + // Verify private profile parent assertThat(mUserManager.getProfileParent(mainUserId)).isNull(); UserInfo parentProfileInfo = mUserManager.getProfileParent(userInfo.id); |