Upgrade profiles to custom user types
Allow existing (manged) profiles to be converted to custom profile
types.
Only convert on system upgrade (using upgradeIfNecessaryLP).
Test: atest UserManagerServiceUserInfoTest; atest
UserManagerServiceUserTypeTest
Bug: 168589581
Change-Id: Ib6431bb4d858b6546509f69dfb7c580aa1c21d48
diff --git a/core/res/res/xml/config_user_types.xml b/core/res/res/xml/config_user_types.xml
index 5fd8a95..71dfc55 100644
--- a/core/res/res/xml/config_user_types.xml
+++ b/core/res/res/xml/config_user_types.xml
@@ -40,7 +40,7 @@
and the PROFILE user android.os.usertype.profile.MANAGED) and creates a new PROFILE user type
(com.example.profilename):
-<user-types>
+<user-types version="0">
<full-type name="android.os.usertype.full.SECONDARY" >
<default-restrictions no_sms="true" />
</full-type>
@@ -65,6 +65,11 @@
<profile-type
name="com.example.profilename"
max-allowed-per-parent="2" />
+
+ <change-user-type
+ from="android.os.usertype.profile.MANAGED"
+ to="com.example.profilename"
+ whenVersionLeq="1" />
</user-types>
Mandatory attributes:
@@ -93,6 +98,10 @@
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'.
+
-->
<user-types>
</user-types>
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c51e75c..cc814bcc 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 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 @@
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 @@
@GuardedBy("mPackagesLock")
private int mNextSerialNumber;
private int mUserVersion = 0;
+ private int mUserTypeVersion = 0;
private IAppOpsService mAppOpsService;
@@ -2565,6 +2566,8 @@
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 @@
*/
@GuardedBy({"mRestrictionsLock", "mPackagesLock"})
private void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions) {
- upgradeIfNecessaryLP(oldGlobalUserRestrictions, mUserVersion);
+ upgradeIfNecessaryLP(oldGlobalUserRestrictions, mUserVersion, mUserTypeVersion);
}
/**
@@ -2636,9 +2639,11 @@
*/
@GuardedBy({"mRestrictionsLock", "mPackagesLock"})
@VisibleForTesting
- void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions, int userVersion) {
+ void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions, int userVersion,
+ int userTypeVersion) {
Set<Integer> 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 @@
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 @@
UserData userData = putUserInfo(system);
mNextSerialNumber = MIN_USER_ID;
mUserVersion = USER_VERSION;
+ mUserTypeVersion = UserTypeFactory.getUserTypeVersion();
Bundle restrictions = new Bundle();
try {
@@ -2991,6 +3008,7 @@
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 @@
// 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 @@
* 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 @@
}
return mDevicePolicyManagerInternal;
}
+
+ @GuardedBy("mUsersLock")
+ @VisibleForTesting
+ void upgradeUserTypesLU(@NonNull List<UserTypeFactory.UserTypeUpgrade> upgradeOps,
+ @NonNull ArrayMap<String, UserTypeDetails> userTypes,
+ final int formerUserTypeVersion,
+ @NonNull Set<Integer> 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 d840e5d..5fa46b9 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 @@
/**
* 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}
* <p>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 ba8a2ba..2e64a70 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 java.io.IOException;
import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -73,14 +74,7 @@
* @return mapping from the name of each user type to its {@link UserTypeDetails} object
*/
public static ArrayMap<String, UserTypeDetails> getUserTypes() {
- final ArrayMap<String, UserTypeDetails.Builder> 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<String, UserTypeDetails.Builder> builders = getDefaultBuilders();
try (XmlResourceParser parser =
Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) {
@@ -94,6 +88,20 @@
return types;
}
+ private static ArrayMap<String, UserTypeDetails.Builder> getDefaultBuilders() {
+ final ArrayMap<String, UserTypeDetails.Builder> 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 @@
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 @@
}
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<UserTypeUpgrade> getUserTypeUpgrades() {
+ final List<UserTypeUpgrade> userUpgrades;
+ try (XmlResourceParser parser =
+ Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) {
+ userUpgrades = parseUserUpgrades(getDefaultBuilders(), parser);
+ }
+ return userUpgrades;
+ }
+
+ @VisibleForTesting
+ static List<UserTypeUpgrade> parseUserUpgrades(
+ ArrayMap<String, UserTypeDetails.Builder> builders, XmlResourceParser parser) {
+ final List<UserTypeUpgrade> 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<String, UserTypeDetails.Builder> 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 a281dca..099ccbe 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 @@
<item res='@*android:color/profile_badge_1' />
</badge-colors>
</full-type>
+
+ <change-user-type from="android.old.name" to="android.test.1" whenVersionLeq="1" />
+
</user-types>
diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
index b6c8fbd..daa7d7b 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.
-->
-<user-types>
+<user-types version="1234">
<profile-type
name='android.test.2'
max-allowed-per-parent='12'
@@ -32,4 +32,7 @@
<default-restrictions no_remove_user='true' no_bluetooth='true' />
</profile-type>
<profile-type name='custom.test.1' max-allowed-per-parent='14' />
+
+ <change-user-type from="android.test.1" to="android.test.2" whenVersionLeq="1233" />
+
</user-types>
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 4fac9dc..dfc25e0 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_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.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 @@
@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 @@
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 @@
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 8e74c90..ee30f68 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.Test;
import org.junit.runner.RunWith;
+import java.util.List;
+
/**
* Tests for {@link UserTypeDetails} and {@link UserTypeFactory}.
*
@@ -358,13 +360,56 @@
() -> 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<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
+ builders.put("name", getMinimalBuilder());
+
+ final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_profile);
+ List<UserTypeFactory.UserTypeUpgrade> 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<String, UserTypeDetails.Builder> 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);