Merge "Upgrade profiles to custom user types"
This commit is contained in:
committed by
Android (Google) Code Review
commit
7afa84bb4d
@@ -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):
|
||||
|
||||
<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 @@ and the PROFILE user android.os.usertype.profile.MANAGED) and creates a new PROF
|
||||
<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 @@ 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'.
|
||||
|
||||
-->
|
||||
<user-types>
|
||||
</user-types>
|
||||
|
||||
@@ -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<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 @@ 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<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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
* <p>Returns {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit.
|
||||
*/
|
||||
public int getMaxAllowedPerParent() {
|
||||
|
||||
@@ -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<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 @@ public final class UserTypeFactory {
|
||||
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 @@ 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<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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<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);
|
||||
|
||||
Reference in New Issue
Block a user