diff --git a/core/res/res/xml/config_user_types.xml b/core/res/res/xml/config_user_types.xml index 5fd8a95af64db..71dfc551ccc14 100644 --- a/core/res/res/xml/config_user_types.xml +++ b/core/res/res/xml/config_user_types.xml @@ -40,7 +40,7 @@ The following example modifies two AOSP user types (the FULL user android.os.use and the PROFILE user android.os.usertype.profile.MANAGED) and creates a new PROFILE user type (com.example.profilename): - + @@ -65,6 +65,11 @@ and the PROFILE user android.os.usertype.profile.MANAGED) and creates a new PROF + + Mandatory attributes: @@ -93,6 +98,10 @@ If this file is updated, the properties of any pre-existing user types will be u Note, however, that default-restrictions refers to the restrictions applied at the time of user creation; therefore, the active restrictions of any pre-existing users will not be updated. +The 'change-user-type' tag should be used in conjunction with the 'version' property of +'user-types'. It defines a type change for all pre-existing users of 'from' type to the new 'to' +type, if the former 'user-type's version of device is less than or equal to 'whenVersionLeq'. + --> diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index c51e75c716cce..cc814bcc7760e 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -119,7 +119,6 @@ import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; @@ -175,6 +174,7 @@ public class UserManagerService extends IUserManager.Stub { private static final String ATTR_CONVERTED_FROM_PRE_CREATED = "convertedFromPreCreated"; private static final String ATTR_GUEST_TO_REMOVE = "guestToRemove"; private static final String ATTR_USER_VERSION = "version"; + private static final String ATTR_USER_TYPE_VERSION = "userTypeConfigVersion"; private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId"; private static final String ATTR_PROFILE_BADGE = "profileBadge"; private static final String ATTR_RESTRICTED_PROFILE_PARENT_ID = "restrictedProfileParentId"; @@ -422,6 +422,7 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mPackagesLock") private int mNextSerialNumber; private int mUserVersion = 0; + private int mUserTypeVersion = 0; private IAppOpsService mAppOpsService; @@ -2565,6 +2566,8 @@ public class UserManagerService extends IUserManager.Stub { parser.getAttributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber); mUserVersion = parser.getAttributeInt(null, ATTR_USER_VERSION, mUserVersion); + mUserTypeVersion = + parser.getAttributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion); } // Pre-O global user restriction were stored as a single bundle (as opposed to per-user @@ -2627,7 +2630,7 @@ public class UserManagerService extends IUserManager.Stub { */ @GuardedBy({"mRestrictionsLock", "mPackagesLock"}) private void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions) { - upgradeIfNecessaryLP(oldGlobalUserRestrictions, mUserVersion); + upgradeIfNecessaryLP(oldGlobalUserRestrictions, mUserVersion, mUserTypeVersion); } /** @@ -2636,9 +2639,11 @@ public class UserManagerService extends IUserManager.Stub { */ @GuardedBy({"mRestrictionsLock", "mPackagesLock"}) @VisibleForTesting - void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions, int userVersion) { + void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions, int userVersion, + int userTypeVersion) { Set userIdsToWrite = new ArraySet<>(); final int originalVersion = mUserVersion; + final int originalUserTypeVersion = mUserTypeVersion; if (userVersion < 1) { // Assign a proper name for the owner, if not initialized correctly before UserData userData = getUserDataNoChecks(UserHandle.USER_SYSTEM); @@ -2771,13 +2776,24 @@ public class UserManagerService extends IUserManager.Stub { userVersion = 9; } + // Done with userVersion changes, moving on to deal with userTypeVersion upgrades + // Upgrade from previous user type to a new user type + final int newUserTypeVersion = UserTypeFactory.getUserTypeVersion(); + if (newUserTypeVersion > userTypeVersion) { + synchronized (mUsersLock) { + upgradeUserTypesLU(UserTypeFactory.getUserTypeUpgrades(), mUserTypes, + userTypeVersion, userIdsToWrite); + } + } + if (userVersion < USER_VERSION) { Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to " + USER_VERSION); } else { mUserVersion = userVersion; + mUserTypeVersion = newUserTypeVersion; - if (originalVersion < mUserVersion) { + if (originalVersion < mUserVersion || originalUserTypeVersion < mUserTypeVersion) { for (int userId : userIdsToWrite) { UserData userData = getUserDataNoChecks(userId); if (userData != null) { @@ -2801,6 +2817,7 @@ public class UserManagerService extends IUserManager.Stub { UserData userData = putUserInfo(system); mNextSerialNumber = MIN_USER_ID; mUserVersion = USER_VERSION; + mUserTypeVersion = UserTypeFactory.getUserTypeVersion(); Bundle restrictions = new Bundle(); try { @@ -2991,6 +3008,7 @@ public class UserManagerService extends IUserManager.Stub { serializer.startTag(null, TAG_USERS); serializer.attributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber); serializer.attributeInt(null, ATTR_USER_VERSION, mUserVersion); + serializer.attributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion); serializer.startTag(null, TAG_GUEST_RESTRICTIONS); synchronized (mGuestRestrictions) { @@ -4957,6 +4975,7 @@ public class UserManagerService extends IUserManager.Stub { // Dump UserTypes pw.println(); + pw.println("User types version: " + mUserTypeVersion); pw.println("User types (" + mUserTypes.size() + " types):"); for (int i = 0; i < mUserTypes.size(); i++) { pw.println(" " + mUserTypes.keyAt(i) + ": "); @@ -5447,6 +5466,9 @@ public class UserManagerService extends IUserManager.Stub { * Returns the maximum number of users allowed for the given userTypeDetails per parent user. * This is applicable for user types that are {@link UserTypeDetails#isProfile()}. * If there is no maximum, {@link UserTypeDetails#UNLIMITED_NUMBER_OF_USERS} is returned. + * Under certain circumstances (such as after a change-user-type) the max value can actually + * be exceeded: this is allowed in order to keep the device in a usable state. + * An error is logged in {@link UserManagerService#upgradeProfileToTypeLU} */ private static int getMaxUsersOfTypePerParent(UserTypeDetails userTypeDetails) { final int defaultMax = userTypeDetails.getMaxAllowedPerParent(); @@ -5534,4 +5556,98 @@ public class UserManagerService extends IUserManager.Stub { } return mDevicePolicyManagerInternal; } + + @GuardedBy("mUsersLock") + @VisibleForTesting + void upgradeUserTypesLU(@NonNull List upgradeOps, + @NonNull ArrayMap userTypes, + final int formerUserTypeVersion, + @NonNull Set userIdsToWrite) { + for (UserTypeFactory.UserTypeUpgrade userTypeUpgrade : upgradeOps) { + if (DBG) { + Slog.i(LOG_TAG, "Upgrade: " + userTypeUpgrade.getFromType() + " to: " + + userTypeUpgrade.getToType() + " maxVersion: " + + userTypeUpgrade.getUpToVersion()); + } + + // upgrade user type if version up to getUpToVersion() + if (formerUserTypeVersion <= userTypeUpgrade.getUpToVersion()) { + for (int i = 0; i < mUsers.size(); i++) { + UserData userData = mUsers.valueAt(i); + if (userTypeUpgrade.getFromType().equals(userData.info.userType)) { + final UserTypeDetails newUserType = userTypes.get( + userTypeUpgrade.getToType()); + + if (newUserType == null) { + throw new IllegalStateException( + "Upgrade destination user type not defined: " + + userTypeUpgrade.getToType()); + } + + upgradeProfileToTypeLU(userData.info, newUserType); + userIdsToWrite.add(userData.info.id); + } + } + } + } + } + + /** + * Changes the user type of a profile to a new user type. + * @param userInfo The user to be updated. + * @param newUserType The new user type. + */ + @GuardedBy("mUsersLock") + @VisibleForTesting + void upgradeProfileToTypeLU(@NonNull UserInfo userInfo, @NonNull UserTypeDetails newUserType) { + Slog.i(LOG_TAG, "Upgrading user " + userInfo.id + + " from " + userInfo.userType + + " to " + newUserType.getName()); + + if (!userInfo.isProfile()) { + throw new IllegalStateException( + "Can only upgrade profile types. " + userInfo.userType + + " is not a profile type."); + } + + // Exceeded maximum profiles for parent user: log error, but allow upgrade + if (!canAddMoreProfilesToUser(newUserType.getName(), userInfo.profileGroupId, false)) { + Slog.w(LOG_TAG, + "Exceeded maximum profiles of type " + newUserType.getName() + " for user " + + userInfo.id + ". Maximum allowed= " + + newUserType.getMaxAllowedPerParent()); + } + + final UserTypeDetails oldUserType = mUserTypes.get(userInfo.userType); + final int oldFlags; + if (oldUserType != null) { + oldFlags = oldUserType.getDefaultUserInfoFlags(); + } else { + // if oldUserType is missing from config_user_types.xml -> can only assume FLAG_PROFILE + oldFlags = UserInfo.FLAG_PROFILE; + } + + //convert userData to newUserType + userInfo.userType = newUserType.getName(); + // remove old default flags and add newUserType's default flags + userInfo.flags = newUserType.getDefaultUserInfoFlags() | (userInfo.flags ^ oldFlags); + + // merge existing base restrictions with the new type's default restrictions + synchronized (mRestrictionsLock) { + if (!UserRestrictionsUtils.isEmpty(newUserType.getDefaultRestrictions())) { + final Bundle newRestrictions = UserRestrictionsUtils.clone( + mBaseUserRestrictions.getRestrictions(userInfo.id)); + UserRestrictionsUtils.merge(newRestrictions, + newUserType.getDefaultRestrictions()); + updateUserRestrictionsInternalLR(newRestrictions, userInfo.id); + if (DBG) { + Slog.i(LOG_TAG, "Updated user " + userInfo.id + + " restrictions to " + newRestrictions); + } + } + } + + // re-compute badge index + userInfo.profileBadge = getFreeProfileBadgeLU(userInfo.profileGroupId, userInfo.userType); + } } diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java index d840e5d8b8828..5fa46b9e46350 100644 --- a/services/core/java/com/android/server/pm/UserTypeDetails.java +++ b/services/core/java/com/android/server/pm/UserTypeDetails.java @@ -174,6 +174,9 @@ public final class UserTypeDetails { /** * Returns the maximum number of this user type allowed per parent (for user types, like * profiles, that have parents). + * Under certain circumstances (such as after a change-user-type) the max value can actually + * be exceeded: this is allowed in order to keep the device in a usable state. + * An error is logged in {@link UserManagerService#upgradeProfileToTypeLU} *

Returns {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit. */ public int getMaxAllowedPerParent() { diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java index ba8a2ba6a14ee..2e64a700de9e9 100644 --- a/services/core/java/com/android/server/pm/UserTypeFactory.java +++ b/services/core/java/com/android/server/pm/UserTypeFactory.java @@ -49,6 +49,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; +import java.util.List; import java.util.function.Consumer; /** @@ -73,14 +74,7 @@ public final class UserTypeFactory { * @return mapping from the name of each user type to its {@link UserTypeDetails} object */ public static ArrayMap getUserTypes() { - final ArrayMap builders = new ArrayMap<>(); - builders.put(USER_TYPE_PROFILE_MANAGED, getDefaultTypeProfileManaged()); - builders.put(USER_TYPE_FULL_SYSTEM, getDefaultTypeFullSystem()); - builders.put(USER_TYPE_FULL_SECONDARY, getDefaultTypeFullSecondary()); - builders.put(USER_TYPE_FULL_GUEST, getDefaultTypeFullGuest()); - builders.put(USER_TYPE_FULL_DEMO, getDefaultTypeFullDemo()); - builders.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted()); - builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless()); + final ArrayMap builders = getDefaultBuilders(); try (XmlResourceParser parser = Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) { @@ -94,6 +88,20 @@ public final class UserTypeFactory { return types; } + private static ArrayMap getDefaultBuilders() { + final ArrayMap builders = new ArrayMap<>(); + + builders.put(USER_TYPE_PROFILE_MANAGED, getDefaultTypeProfileManaged()); + builders.put(USER_TYPE_FULL_SYSTEM, getDefaultTypeFullSystem()); + builders.put(USER_TYPE_FULL_SECONDARY, getDefaultTypeFullSecondary()); + builders.put(USER_TYPE_FULL_GUEST, getDefaultTypeFullGuest()); + builders.put(USER_TYPE_FULL_DEMO, getDefaultTypeFullDemo()); + builders.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted()); + builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless()); + + return builders; + } + /** * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_MANAGED} * configuration. @@ -232,6 +240,10 @@ public final class UserTypeFactory { isProfile = true; } else if ("full-type".equals(elementName)) { isProfile = false; + } else if ("change-user-type".equals(elementName)) { + // parsed in parseUserUpgrades + XmlUtils.skipCurrentTag(parser); + continue; } else { Slog.w(LOG_TAG, "Skipping unknown element " + elementName + " in " + parser.getPositionDescription()); @@ -387,4 +399,132 @@ public final class UserTypeFactory { } fcn.accept(result); } + + /** + * Returns the user type version of the config XML file. + * @return user type version defined in XML file, 0 if none. + */ + public static int getUserTypeVersion() { + try (XmlResourceParser parser = + Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) { + return getUserTypeVersion(parser); + } + } + + @VisibleForTesting + static int getUserTypeVersion(XmlResourceParser parser) { + int version = 0; + + try { + XmlUtils.beginDocument(parser, "user-types"); + String versionValue = parser.getAttributeValue(null, "version"); + if (versionValue != null) { + try { + version = Integer.parseInt(versionValue); + } catch (NumberFormatException e) { + Slog.e(LOG_TAG, "Cannot parse value of '" + versionValue + "' for version in " + + parser.getPositionDescription(), e); + throw e; + } + } + } catch (XmlPullParserException | IOException e) { + Slog.w(LOG_TAG, "Cannot read user type configuration file.", e); + } + + return version; + } + + /** + * Obtains the user type upgrades for this device. + * @return The list of user type upgrades. + */ + public static List getUserTypeUpgrades() { + final List userUpgrades; + try (XmlResourceParser parser = + Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) { + userUpgrades = parseUserUpgrades(getDefaultBuilders(), parser); + } + return userUpgrades; + } + + @VisibleForTesting + static List parseUserUpgrades( + ArrayMap builders, XmlResourceParser parser) { + final List userUpgrades = new ArrayList<>(); + + try { + XmlUtils.beginDocument(parser, "user-types"); + for (XmlUtils.nextElement(parser); + parser.getEventType() != XmlResourceParser.END_DOCUMENT; + XmlUtils.nextElement(parser)) { + final String elementName = parser.getName(); + if ("change-user-type".equals(elementName)) { + final String fromType = parser.getAttributeValue(null, "from"); + final String toType = parser.getAttributeValue(null, "to"); + // Check that the base type doesn't change. + // Currently, only the base type of PROFILE is supported. + validateUserTypeIsProfile(fromType, builders); + validateUserTypeIsProfile(toType, builders); + + final int maxVersionToConvert; + try { + maxVersionToConvert = Integer.parseInt( + parser.getAttributeValue(null, "whenVersionLeq")); + } catch (NumberFormatException e) { + Slog.e(LOG_TAG, "Cannot parse value of whenVersionLeq in " + + parser.getPositionDescription(), e); + throw e; + } + + UserTypeUpgrade userTypeUpgrade = new UserTypeUpgrade(fromType, toType, + maxVersionToConvert); + userUpgrades.add(userTypeUpgrade); + continue; + } else { + XmlUtils.skipCurrentTag(parser); + continue; + } + } + } catch (XmlPullParserException | IOException e) { + Slog.w(LOG_TAG, "Cannot read user type configuration file.", e); + } + + return userUpgrades; + } + + private static void validateUserTypeIsProfile(String userType, + ArrayMap builders) { + UserTypeDetails.Builder builder = builders.get(userType); + if (builder != null && builder.getBaseType() != FLAG_PROFILE) { + throw new IllegalArgumentException("Illegal upgrade of user type " + userType + + " : Can only upgrade profiles user types"); + } + } + + /** + * Contains details required for an upgrade operation for {@link UserTypeDetails}; + */ + public static class UserTypeUpgrade { + private final String mFromType; + private final String mToType; + private final int mUpToVersion; + + public UserTypeUpgrade(String fromType, String toType, int upToVersion) { + mFromType = fromType; + mToType = toType; + mUpToVersion = upToVersion; + } + + public String getFromType() { + return mFromType; + } + + public String getToType() { + return mToType; + } + + public int getUpToVersion() { + return mUpToVersion; + } + } } diff --git a/services/tests/servicestests/res/xml/usertypes_test_full.xml b/services/tests/servicestests/res/xml/usertypes_test_full.xml index a281dcaafb3d7..099ccbe5b5f60 100644 --- a/services/tests/servicestests/res/xml/usertypes_test_full.xml +++ b/services/tests/servicestests/res/xml/usertypes_test_full.xml @@ -22,4 +22,7 @@ + + + diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml index b6c8fbdcdd200..daa7d7b7341a2 100644 --- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml +++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - + + + + diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java index 4fac9dc391e32..dfc25e0c7cb6d 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java @@ -21,6 +21,7 @@ import static android.content.pm.UserInfo.FLAG_DISABLED; import static android.content.pm.UserInfo.FLAG_EPHEMERAL; import static android.content.pm.UserInfo.FLAG_FULL; import static android.content.pm.UserInfo.FLAG_GUEST; +import static android.content.pm.UserInfo.FLAG_INITIALIZED; import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static android.content.pm.UserInfo.FLAG_PROFILE; import static android.content.pm.UserInfo.FLAG_RESTRICTED; @@ -44,6 +45,7 @@ import android.content.pm.UserInfo.UserInfoFlag; import android.os.Looper; import android.os.Parcel; import android.os.UserHandle; +import android.os.UserManager; import android.text.TextUtils; import androidx.test.InstrumentationRegistry; @@ -206,6 +208,8 @@ public class UserManagerServiceUserInfoTest { @Test public void testUpgradeIfNecessaryLP_9() { final int versionToTest = 9; + // do not trigger a user type upgrade + final int userTypeVersion = UserTypeFactory.getUserTypeVersion(); mUserManagerService.putUserInfo(createUser(100, FLAG_MANAGED_PROFILE, null)); mUserManagerService.putUserInfo(createUser(101, @@ -216,7 +220,7 @@ public class UserManagerServiceUserInfoTest { mUserManagerService.putUserInfo(createUser(105, FLAG_SYSTEM | FLAG_FULL, null)); mUserManagerService.putUserInfo(createUser(106, FLAG_DEMO | FLAG_FULL, null)); - mUserManagerService.upgradeIfNecessaryLP(null, versionToTest - 1); + mUserManagerService.upgradeIfNecessaryLP(null, versionToTest - 1, userTypeVersion); assertTrue(mUserManagerService.isUserOfType(100, USER_TYPE_PROFILE_MANAGED)); assertTrue((mUserManagerService.getUserInfo(100).flags & FLAG_PROFILE) != 0); @@ -278,4 +282,86 @@ public class UserManagerServiceUserInfoTest { two.convertedFromPreCreated); } } + + /** Tests upgrading profile types */ + @Test + public void testUpgradeProfileType_updateTypeAndFlags() { + final int userId = 42; + final String newUserTypeName = "new.user.type"; + final String oldUserTypeName = USER_TYPE_PROFILE_MANAGED; + + UserTypeDetails.Builder oldUserTypeBuilder = new UserTypeDetails.Builder() + .setName(oldUserTypeName) + .setBaseType(FLAG_PROFILE) + .setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE) + .setMaxAllowedPerParent(32) + .setIconBadge(401) + .setBadgeColors(402, 403, 404) + .setBadgeLabels(23, 24, 25); + UserTypeDetails oldUserType = oldUserTypeBuilder.createUserTypeDetails(); + + UserInfo userInfo = createUser(userId, + oldUserType.getDefaultUserInfoFlags() | FLAG_INITIALIZED, oldUserTypeName); + mUserManagerService.putUserInfo(userInfo); + + UserTypeDetails.Builder newUserTypeBuilder = new UserTypeDetails.Builder() + .setName(newUserTypeName) + .setBaseType(FLAG_PROFILE) + .setMaxAllowedPerParent(32) + .setIconBadge(401) + .setBadgeColors(402, 403, 404) + .setBadgeLabels(23, 24, 25); + UserTypeDetails newUserType = newUserTypeBuilder.createUserTypeDetails(); + + mUserManagerService.upgradeProfileToTypeLU(userInfo, newUserType); + + assertTrue(mUserManagerService.isUserOfType(userId, newUserTypeName)); + assertTrue((mUserManagerService.getUserInfo(userId).flags & FLAG_PROFILE) != 0); + assertTrue((mUserManagerService.getUserInfo(userId).flags & FLAG_MANAGED_PROFILE) == 0); + assertTrue((mUserManagerService.getUserInfo(userId).flags & FLAG_INITIALIZED) != 0); + } + + @Test + public void testUpgradeProfileType_updateRestrictions() { + final int userId = 42; + final String newUserTypeName = "new.user.type"; + final String oldUserTypeName = USER_TYPE_PROFILE_MANAGED; + + UserTypeDetails.Builder oldUserTypeBuilder = new UserTypeDetails.Builder() + .setName(oldUserTypeName) + .setBaseType(FLAG_PROFILE) + .setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE) + .setMaxAllowedPerParent(32) + .setIconBadge(401) + .setBadgeColors(402, 403, 404) + .setBadgeLabels(23, 24, 25); + UserTypeDetails oldUserType = oldUserTypeBuilder.createUserTypeDetails(); + + UserInfo userInfo = createUser(userId, oldUserType.getDefaultUserInfoFlags(), + oldUserTypeName); + mUserManagerService.putUserInfo(userInfo); + mUserManagerService.setUserRestriction(UserManager.DISALLOW_CAMERA, true, userId); + mUserManagerService.setUserRestriction(UserManager.DISALLOW_PRINTING, true, userId); + + UserTypeDetails.Builder newUserTypeBuilder = new UserTypeDetails.Builder() + .setName(newUserTypeName) + .setBaseType(FLAG_PROFILE) + .setMaxAllowedPerParent(32) + .setIconBadge(401) + .setBadgeColors(402, 403, 404) + .setBadgeLabels(23, 24, 25) + .setDefaultRestrictions( + UserManagerServiceUserTypeTest.makeRestrictionsBundle( + UserManager.DISALLOW_WALLPAPER)); + UserTypeDetails newUserType = newUserTypeBuilder.createUserTypeDetails(); + + mUserManagerService.upgradeProfileToTypeLU(userInfo, newUserType); + + assertTrue(mUserManagerService.getUserRestrictions(userId).getBoolean( + UserManager.DISALLOW_PRINTING)); + assertTrue(mUserManagerService.getUserRestrictions(userId).getBoolean( + UserManager.DISALLOW_CAMERA)); + assertTrue(mUserManagerService.getUserRestrictions(userId).getBoolean( + UserManager.DISALLOW_WALLPAPER)); + } } 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 8e74c903534fb..ee30f68de7a04 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java @@ -51,6 +51,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.List; + /** * Tests for {@link UserTypeDetails} and {@link UserTypeFactory}. * @@ -358,13 +360,56 @@ public class UserManagerServiceUserTypeTest { () -> UserTypeFactory.customizeBuilders(builders, parser)); } + @Test + public void testUserTypeFactoryVersion_versionMissing() { + final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_eraseArray); + assertEquals(0, UserTypeFactory.getUserTypeVersion(parser)); + } + + @Test + public void testUserTypeFactoryVersion_versionPresent() { + final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_profile); + assertEquals(1234, UserTypeFactory.getUserTypeVersion(parser)); + } + + @Test + public void testUserTypeFactoryUpgrades_validUpgrades() { + final ArrayMap builders = new ArrayMap<>(); + builders.put("name", getMinimalBuilder()); + + final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_profile); + List upgrades = UserTypeFactory.parseUserUpgrades(builders, + parser); + + assertFalse(upgrades.isEmpty()); + UserTypeFactory.UserTypeUpgrade upgrade = upgrades.get(0); + assertEquals("android.test.1", upgrade.getFromType()); + assertEquals("android.test.2", upgrade.getToType()); + assertEquals(1233, upgrade.getUpToVersion()); + } + + @Test + public void testUserTypeFactoryUpgrades_illegalBaseTypeUpgrade() { + final String userTypeFull = "android.test.1"; + final ArrayMap builders = new ArrayMap<>(); + builders.put(userTypeFull, new UserTypeDetails.Builder() + .setName(userTypeFull) + .setBaseType(FLAG_FULL)); + + final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_full); + + // parser is illegal because the "to" upgrade type is not a profile, but a full user + assertThrows(IllegalArgumentException.class, + () -> UserTypeFactory.parseUserUpgrades(builders, parser)); + } + /** Returns a minimal {@link UserTypeDetails.Builder} that can legitimately be created. */ private UserTypeDetails.Builder getMinimalBuilder() { return new UserTypeDetails.Builder().setName("name").setBaseType(FLAG_FULL); } /** Creates a Bundle of the given String restrictions, each set to true. */ - private Bundle makeRestrictionsBundle(String ... restrictions) { + public static Bundle makeRestrictionsBundle(String ... restrictions) { final Bundle bundle = new Bundle(); for (String restriction : restrictions) { bundle.putBoolean(restriction, true);