From 02c8990bd6125f05cb32561727d559dc9327a601 Mon Sep 17 00:00:00 2001 From: Kenny Guy Date: Tue, 15 Nov 2016 19:36:38 +0000 Subject: [PATCH] Allow overriding max profile in debugable builds. Support a system property on debugable builds to override the max number of managed profiles to allow easier dogfooding of multiple profiles. Support 3 different badge colours for managed profiles. Bug: 30473760 Test: runtest -c com.android.server.pm.UserManagerServiceCreateProfileTest frameworks-services Test: runtest -c com.android.server.pm.UserManagerServiceUserInfoTest frameworks-services Test: manual - attempting to create 2 profiles with adb fails, passes once I set the property. Change-Id: Ie7fb19048a04a01572666f229283152254d0ffc3 --- .../app/ApplicationPackageManager.java | 83 +++- .../android/content/pm/PackageManager.java | 26 - core/java/android/content/pm/UserInfo.java | 5 + core/java/android/os/IUserManager.aidl | 1 + core/java/android/os/UserManager.java | 17 + core/res/res/drawable/ic_corp_badge_case.xml | 30 ++ core/res/res/drawable/ic_corp_badge_color.xml | 24 + .../drawable/ic_corp_badge_no_background.xml | 6 +- ..._badge.xml => ic_corp_icon_badge_case.xml} | 13 +- .../res/drawable/ic_corp_icon_badge_color.xml | 24 + .../drawable/ic_corp_icon_badge_shadow.xml | 29 ++ core/res/res/values/colors.xml | 5 + core/res/res/values/strings.xml | 2 + core/res/res/values/symbols.xml | 14 +- .../android/server/pm/UserManagerService.java | 448 ++++++++++-------- .../UserManagerServiceCreateProfileTest.java | 200 ++++++++ .../pm/UserManagerServiceUserInfoTest.java | 130 +++++ .../android/test/mock/MockPackageManager.java | 8 - .../bridge/android/BridgePackageManager.java | 6 - 19 files changed, 801 insertions(+), 270 deletions(-) create mode 100644 core/res/res/drawable/ic_corp_badge_case.xml create mode 100644 core/res/res/drawable/ic_corp_badge_color.xml rename core/res/res/drawable/{ic_corp_icon_badge.xml => ic_corp_icon_badge_case.xml} (71%) create mode 100644 core/res/res/drawable/ic_corp_icon_badge_color.xml create mode 100644 core/res/res/drawable/ic_corp_icon_badge_shadow.xml create mode 100644 services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java create mode 100644 services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index b199984740f24..627e6611c930e 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -58,9 +58,11 @@ import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -81,6 +83,7 @@ import android.view.Display; import dalvik.system.VMRuntime; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.internal.util.Preconditions; import com.android.internal.util.UserIcons; @@ -1174,22 +1177,22 @@ public class ApplicationPackageManager extends PackageManager { return getApplicationLogo(getApplicationInfo(packageName, sDefaultFlags)); } - @Override - public Drawable getManagedUserBadgedDrawable(Drawable drawable, Rect badgeLocation, - int badgeDensity) { - Drawable badgeDrawable = getDrawableForDensity( - com.android.internal.R.drawable.ic_corp_badge, badgeDensity); - return getBadgedDrawable(drawable, badgeDrawable, badgeLocation, true); - } - @Override public Drawable getUserBadgedIcon(Drawable icon, UserHandle user) { - final int badgeResId = getBadgeResIdForUser(user.getIdentifier()); - if (badgeResId == 0) { + if (!isManagedProfile(user.getIdentifier())) { return icon; } - Drawable badgeIcon = getDrawable("system", badgeResId, null); - return getBadgedDrawable(icon, badgeIcon, null, true); + Drawable badgeShadow = getDrawable("system", + com.android.internal.R.drawable.ic_corp_icon_badge_shadow, null); + Drawable badgeColor = getDrawable("system", + com.android.internal.R.drawable.ic_corp_icon_badge_color, null); + badgeColor.setTint(getUserBadgeColor(user)); + Drawable badgeForeground = getDrawable("system", + com.android.internal.R.drawable.ic_corp_icon_badge_case, null); + + Drawable badge = new LayerDrawable( + new Drawable[] {badgeShadow, badgeColor, badgeForeground }); + return getBadgedDrawable(icon, badge, null, true); } @Override @@ -1202,16 +1205,53 @@ public class ApplicationPackageManager extends PackageManager { return getBadgedDrawable(drawable, badgeDrawable, badgeLocation, true); } + // Should have enough colors to cope with UserManagerService.getMaxManagedProfiles() + @VisibleForTesting + public static final int[] CORP_BADGE_COLORS = new int[] { + com.android.internal.R.color.profile_badge_1, + com.android.internal.R.color.profile_badge_2, + com.android.internal.R.color.profile_badge_3 + }; + + @VisibleForTesting + public static final int[] CORP_BADGE_LABEL_RES_ID = new int[] { + com.android.internal.R.string.managed_profile_label_badge, + com.android.internal.R.string.managed_profile_label_badge_2, + com.android.internal.R.string.managed_profile_label_badge_3 + }; + + private int getUserBadgeColor(UserHandle user) { + int badge = getUserManager().getManagedProfileBadge(user.getIdentifier()); + if (badge < 0) { + badge = 0; + } + int resourceId = CORP_BADGE_COLORS[badge % CORP_BADGE_COLORS.length]; + return Resources.getSystem().getColor(resourceId, null); + } + @Override public Drawable getUserBadgeForDensity(UserHandle user, int density) { - return getManagedProfileIconForDensity(user, com.android.internal.R.drawable.ic_corp_badge, - density); + Drawable badgeColor = getManagedProfileIconForDensity(user, + com.android.internal.R.drawable.ic_corp_badge_color, density); + if (badgeColor == null) { + return null; + } + badgeColor.setTint(getUserBadgeColor(user)); + Drawable badgeForeground = getDrawableForDensity( + com.android.internal.R.drawable.ic_corp_badge_case, density); + Drawable badge = new LayerDrawable( + new Drawable[] {badgeColor, badgeForeground }); + return badge; } @Override public Drawable getUserBadgeForDensityNoBackground(UserHandle user, int density) { - return getManagedProfileIconForDensity(user, + Drawable badge = getManagedProfileIconForDensity(user, com.android.internal.R.drawable.ic_corp_badge_no_background, density); + if (badge != null) { + badge.setTint(getUserBadgeColor(user)); + } + return badge; } private Drawable getDrawableForDensity(int drawableId, int density) { @@ -1231,8 +1271,9 @@ public class ApplicationPackageManager extends PackageManager { @Override public CharSequence getUserBadgedLabel(CharSequence label, UserHandle user) { if (isManagedProfile(user.getIdentifier())) { - return Resources.getSystem().getString( - com.android.internal.R.string.managed_profile_label_badge, label); + int badge = getUserManager().getManagedProfileBadge(user.getIdentifier()); + int resourceId = CORP_BADGE_LABEL_RES_ID[badge % CORP_BADGE_LABEL_RES_ID.length]; + return Resources.getSystem().getString(resourceId, label); } return label; } @@ -2355,14 +2396,6 @@ public class ApplicationPackageManager extends PackageManager { return drawable; } - private int getBadgeResIdForUser(int userId) { - // Return the framework-provided badge. - if (isManagedProfile(userId)) { - return com.android.internal.R.drawable.ic_corp_icon_badge; - } - return 0; - } - private boolean isManagedProfile(int userId) { return getUserManager().isManagedProfile(userId); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index b3dd0e56caf9c..5087bc8555a6c 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4521,32 +4521,6 @@ public abstract class PackageManager { public abstract Drawable getApplicationLogo(String packageName) throws NameNotFoundException; - /** - * Returns a managed-user-style badged copy of the given drawable allowing the user to - * distinguish it from the original drawable. - * The caller can specify the location in the bounds of the drawable to be - * badged where the badge should be applied as well as the density of the - * badge to be used. - *

- * If the original drawable is a BitmapDrawable and the backing bitmap is - * mutable as per {@link android.graphics.Bitmap#isMutable()}, the badging - * is performed in place and the original drawable is returned. - *

- * - * @param drawable The drawable to badge. - * @param badgeLocation Where in the bounds of the badged drawable to place - * the badge. If it's {@code null}, the badge is applied on top of the entire - * drawable being badged. - * @param badgeDensity The optional desired density for the badge as per - * {@link android.util.DisplayMetrics#densityDpi}. If it's not positive, - * the density of the display is used. - * @return A drawable that combines the original drawable and a badge as - * determined by the system. - * @hide - */ - public abstract Drawable getManagedUserBadgedDrawable(Drawable drawable, Rect badgeLocation, - int badgeDensity); - /** * If the target user is a managed profile, then this returns a badged copy of the given icon * to be able to distinguish it from the original icon. For badging an arbitrary drawable use diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index e796fa7db1aaf..f34b590e693c5 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -109,6 +109,8 @@ public class UserInfo implements Parcelable { */ public int profileGroupId; public int restrictedProfileParentId; + /** Which profile badge color/label to use. */ + public int profileBadge; /** User is only partially created. */ public boolean partial; @@ -233,6 +235,7 @@ public class UserInfo implements Parcelable { profileGroupId = orig.profileGroupId; restrictedProfileParentId = orig.restrictedProfileParentId; guestToRemove = orig.guestToRemove; + profileBadge = orig.profileBadge; } public UserHandle getUserHandle() { @@ -261,6 +264,7 @@ public class UserInfo implements Parcelable { dest.writeInt(profileGroupId); dest.writeInt(guestToRemove ? 1 : 0); dest.writeInt(restrictedProfileParentId); + dest.writeInt(profileBadge); } public static final Parcelable.Creator CREATOR @@ -286,5 +290,6 @@ public class UserInfo implements Parcelable { profileGroupId = source.readInt(); guestToRemove = source.readInt() != 0; restrictedProfileParentId = source.readInt(); + profileBadge = source.readInt(); } } diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 65c609308b634..9dafe29435125 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -86,4 +86,5 @@ interface IUserManager { UserInfo createProfileForUserEvenWhenDisallowed(in String name, int flags, int userHandle, in String[] disallowedPackages); boolean isUserUnlockingOrUnlocked(int userId); + int getManagedProfileBadge(int userId); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 50eb7cf5e6470..a79b0c4727411 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -947,6 +947,23 @@ public class UserManager { } } + /** + * Gets badge for a managed profile. + * Requires {@link android.Manifest.permission#MANAGE_USERS} permission, otherwise the caller + * must be in the same profile group of specified user. + * + * @return which badge to use for the managed profile badge id will be less than + * UserManagerService.getMaxManagedProfiles() + * @hide + */ + public int getManagedProfileBadge(@UserIdInt int userId) { + try { + return mService.getManagedProfileBadge(userId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + /** * Checks if the calling app is running as an ephemeral user. * diff --git a/core/res/res/drawable/ic_corp_badge_case.xml b/core/res/res/drawable/ic_corp_badge_case.xml new file mode 100644 index 0000000000000..0b6028cc5d2a8 --- /dev/null +++ b/core/res/res/drawable/ic_corp_badge_case.xml @@ -0,0 +1,30 @@ + + + + + + diff --git a/core/res/res/drawable/ic_corp_badge_color.xml b/core/res/res/drawable/ic_corp_badge_color.xml new file mode 100644 index 0000000000000..b6c79696c0224 --- /dev/null +++ b/core/res/res/drawable/ic_corp_badge_color.xml @@ -0,0 +1,24 @@ + + + + diff --git a/core/res/res/drawable/ic_corp_badge_no_background.xml b/core/res/res/drawable/ic_corp_badge_no_background.xml index b1bddfc98fca2..78322a9920c87 100644 --- a/core/res/res/drawable/ic_corp_badge_no_background.xml +++ b/core/res/res/drawable/ic_corp_badge_no_background.xml @@ -20,11 +20,11 @@ Copyright (C) 2016 The Android Open Source Project android:viewportHeight="24.0"> + android:fillColor="#FFFFFF"/> + android:fillColor="#FFFFFF"/> - \ No newline at end of file + diff --git a/core/res/res/drawable/ic_corp_icon_badge.xml b/core/res/res/drawable/ic_corp_icon_badge_case.xml similarity index 71% rename from core/res/res/drawable/ic_corp_icon_badge.xml rename to core/res/res/drawable/ic_corp_icon_badge_case.xml index 02735459e04cf..d62eda4a12ba0 100644 --- a/core/res/res/drawable/ic_corp_icon_badge.xml +++ b/core/res/res/drawable/ic_corp_icon_badge_case.xml @@ -1,5 +1,5 @@ + + + diff --git a/core/res/res/drawable/ic_corp_icon_badge_shadow.xml b/core/res/res/drawable/ic_corp_icon_badge_shadow.xml new file mode 100644 index 0000000000000..a546cdd29a8e2 --- /dev/null +++ b/core/res/res/drawable/ic_corp_icon_badge_shadow.xml @@ -0,0 +1,29 @@ + + + + + diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 0995bc3f07992..fa9cac2f8341b 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -165,6 +165,11 @@ #ff9e9e9e #ffffffff + + #ffff5722 + #ff000000 + #ff22f033 + #ff00796b #ff00695c diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 4070d4802732d..8546aa50aa5c4 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4161,6 +4161,8 @@ [CHAR LIMIT=20] --> Work %1$s + 2nd Work %1$s + 3rd Work %1$s -- diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a28a6fdc219aa..f6fd64b07016d 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1017,6 +1017,8 @@ + + @@ -1269,12 +1271,15 @@ - + + + - + + + - @@ -1301,6 +1306,9 @@ + + + diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 5b47b6f1da010..20afed736bab5 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -63,6 +63,7 @@ import android.os.SELinux; import android.os.ServiceManager; import android.os.ShellCallback; import android.os.ShellCommand; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; @@ -110,6 +111,8 @@ import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; @@ -149,6 +152,7 @@ public class UserManagerService extends IUserManager.Stub { private static final String ATTR_GUEST_TO_REMOVE = "guestToRemove"; private static final String ATTR_USER_VERSION = "version"; 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"; private static final String ATTR_SEED_ACCOUNT_NAME = "seedAccountName"; private static final String ATTR_SEED_ACCOUNT_TYPE = "seedAccountType"; @@ -203,7 +207,8 @@ public class UserManagerService extends IUserManager.Stub { // Maximum number of managed profiles permitted per user is 1. This cannot be increased // without first making sure that the rest of the framework is prepared for it. - private static final int MAX_MANAGED_PROFILES = 1; + @VisibleForTesting + static final int MAX_MANAGED_PROFILES = 1; static final int WRITE_USER_MSG = 1; static final int WRITE_USER_DELAY = 2*1000; // 2 seconds @@ -232,7 +237,8 @@ public class UserManagerService extends IUserManager.Stub { * User-related information that is used for persisting to flash. Only UserInfo is * directly exposed to other system apps. */ - private static class UserData { + @VisibleForTesting + static class UserData { // Basic user information and properties UserInfo info; // Account name used when there is a strong association between a user and an account @@ -873,6 +879,22 @@ public class UserManagerService extends IUserManager.Stub { } } + @Override + public int getManagedProfileBadge(@UserIdInt int userId) { + int callingUserId = UserHandle.getCallingUserId(); + if (callingUserId != userId && !hasManageUsersPermission()) { + if (!isSameProfileGroupNoChecks(callingUserId, userId)) { + throw new SecurityException( + "You need MANAGE_USERS permission to: check if specified user a " + + "managed profile outside your profile group"); + } + } + synchronized (mUsersLock) { + UserInfo userInfo = getUserInfoLU(userId); + return userInfo != null ? userInfo.profileBadge : 0; + } + } + @Override public boolean isManagedProfile(int userId) { int callingUserId = UserHandle.getCallingUserId(); @@ -1464,7 +1486,7 @@ public class UserManagerService extends IUserManager.Stub { // Limit number of managed profiles that can be created final int managedProfilesCount = getProfiles(userId, true).size() - 1; final int profilesRemovedCount = managedProfilesCount > 0 && allowedToRemoveOne ? 1 : 0; - if (managedProfilesCount - profilesRemovedCount >= MAX_MANAGED_PROFILES) { + if (managedProfilesCount - profilesRemovedCount >= getMaxManagedProfiles()) { return false; } synchronized(mUsersLock) { @@ -1859,13 +1881,6 @@ public class UserManagerService extends IUserManager.Stub { } } - /* - * Writes the user file in this format: - * - * - * Primary - * - */ private void writeUserLP(UserData userData) { if (DBG) { debug("writeUserLP " + userData); @@ -1875,78 +1890,7 @@ public class UserManagerService extends IUserManager.Stub { try { fos = userFile.startWrite(); final BufferedOutputStream bos = new BufferedOutputStream(fos); - - // XmlSerializer serializer = XmlUtils.serializerInstance(); - final XmlSerializer serializer = new FastXmlSerializer(); - serializer.setOutput(bos, StandardCharsets.UTF_8.name()); - serializer.startDocument(null, true); - serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - - final UserInfo userInfo = userData.info; - serializer.startTag(null, TAG_USER); - serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id)); - serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber)); - serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags)); - serializer.attribute(null, ATTR_CREATION_TIME, Long.toString(userInfo.creationTime)); - serializer.attribute(null, ATTR_LAST_LOGGED_IN_TIME, - Long.toString(userInfo.lastLoggedInTime)); - if (userInfo.lastLoggedInFingerprint != null) { - serializer.attribute(null, ATTR_LAST_LOGGED_IN_FINGERPRINT, - userInfo.lastLoggedInFingerprint); - } - if (userInfo.iconPath != null) { - serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath); - } - if (userInfo.partial) { - serializer.attribute(null, ATTR_PARTIAL, "true"); - } - if (userInfo.guestToRemove) { - serializer.attribute(null, ATTR_GUEST_TO_REMOVE, "true"); - } - if (userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) { - serializer.attribute(null, ATTR_PROFILE_GROUP_ID, - Integer.toString(userInfo.profileGroupId)); - } - if (userInfo.restrictedProfileParentId != UserInfo.NO_PROFILE_GROUP_ID) { - serializer.attribute(null, ATTR_RESTRICTED_PROFILE_PARENT_ID, - Integer.toString(userInfo.restrictedProfileParentId)); - } - // Write seed data - if (userData.persistSeedData) { - if (userData.seedAccountName != null) { - serializer.attribute(null, ATTR_SEED_ACCOUNT_NAME, userData.seedAccountName); - } - if (userData.seedAccountType != null) { - serializer.attribute(null, ATTR_SEED_ACCOUNT_TYPE, userData.seedAccountType); - } - } - if (userInfo.name != null) { - serializer.startTag(null, TAG_NAME); - serializer.text(userInfo.name); - serializer.endTag(null, TAG_NAME); - } - synchronized (mRestrictionsLock) { - UserRestrictionsUtils.writeRestrictions(serializer, - mBaseUserRestrictions.get(userInfo.id), TAG_RESTRICTIONS); - UserRestrictionsUtils.writeRestrictions(serializer, - mDevicePolicyLocalUserRestrictions.get(userInfo.id), - TAG_DEVICE_POLICY_RESTRICTIONS); - } - - if (userData.account != null) { - serializer.startTag(null, TAG_ACCOUNT); - serializer.text(userData.account); - serializer.endTag(null, TAG_ACCOUNT); - } - - if (userData.persistSeedData && userData.seedAccountOptions != null) { - serializer.startTag(null, TAG_SEED_ACCOUNT_OPTIONS); - userData.seedAccountOptions.saveToXml(serializer); - serializer.endTag(null, TAG_SEED_ACCOUNT_OPTIONS); - } - serializer.endTag(null, TAG_USER); - - serializer.endDocument(); + writeUserLP(userData, bos); userFile.finishWrite(fos); } catch (Exception ioe) { Slog.e(LOG_TAG, "Error writing user info " + userData.info.id, ioe); @@ -1954,6 +1898,92 @@ public class UserManagerService extends IUserManager.Stub { } } + /* + * Writes the user file in this format: + * + * + * Primary + * + */ + @VisibleForTesting + void writeUserLP(UserData userData, OutputStream os) + throws IOException, XmlPullParserException { + // XmlSerializer serializer = XmlUtils.serializerInstance(); + final XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(os, StandardCharsets.UTF_8.name()); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + final UserInfo userInfo = userData.info; + serializer.startTag(null, TAG_USER); + serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id)); + serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber)); + serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags)); + serializer.attribute(null, ATTR_CREATION_TIME, Long.toString(userInfo.creationTime)); + serializer.attribute(null, ATTR_LAST_LOGGED_IN_TIME, + Long.toString(userInfo.lastLoggedInTime)); + if (userInfo.lastLoggedInFingerprint != null) { + serializer.attribute(null, ATTR_LAST_LOGGED_IN_FINGERPRINT, + userInfo.lastLoggedInFingerprint); + } + if (userInfo.iconPath != null) { + serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath); + } + if (userInfo.partial) { + serializer.attribute(null, ATTR_PARTIAL, "true"); + } + if (userInfo.guestToRemove) { + serializer.attribute(null, ATTR_GUEST_TO_REMOVE, "true"); + } + if (userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) { + serializer.attribute(null, ATTR_PROFILE_GROUP_ID, + Integer.toString(userInfo.profileGroupId)); + } + serializer.attribute(null, ATTR_PROFILE_BADGE, + Integer.toString(userInfo.profileBadge)); + if (userInfo.restrictedProfileParentId != UserInfo.NO_PROFILE_GROUP_ID) { + serializer.attribute(null, ATTR_RESTRICTED_PROFILE_PARENT_ID, + Integer.toString(userInfo.restrictedProfileParentId)); + } + // Write seed data + if (userData.persistSeedData) { + if (userData.seedAccountName != null) { + serializer.attribute(null, ATTR_SEED_ACCOUNT_NAME, userData.seedAccountName); + } + if (userData.seedAccountType != null) { + serializer.attribute(null, ATTR_SEED_ACCOUNT_TYPE, userData.seedAccountType); + } + } + if (userInfo.name != null) { + serializer.startTag(null, TAG_NAME); + serializer.text(userInfo.name); + serializer.endTag(null, TAG_NAME); + } + synchronized (mRestrictionsLock) { + UserRestrictionsUtils.writeRestrictions(serializer, + mBaseUserRestrictions.get(userInfo.id), TAG_RESTRICTIONS); + UserRestrictionsUtils.writeRestrictions(serializer, + mDevicePolicyLocalUserRestrictions.get(userInfo.id), + TAG_DEVICE_POLICY_RESTRICTIONS); + } + + if (userData.account != null) { + serializer.startTag(null, TAG_ACCOUNT); + serializer.text(userData.account); + serializer.endTag(null, TAG_ACCOUNT); + } + + if (userData.persistSeedData && userData.seedAccountOptions != null) { + serializer.startTag(null, TAG_SEED_ACCOUNT_OPTIONS); + userData.seedAccountOptions.saveToXml(serializer); + serializer.endTag(null, TAG_SEED_ACCOUNT_OPTIONS); + } + + serializer.endTag(null, TAG_USER); + + serializer.endDocument(); + } + /* * Writes the user list file in this format: * @@ -2020,6 +2050,25 @@ public class UserManagerService extends IUserManager.Stub { } private UserData readUserLP(int id) { + FileInputStream fis = null; + try { + AtomicFile userFile = + new AtomicFile(new File(mUsersDir, Integer.toString(id) + XML_SUFFIX)); + fis = userFile.openRead(); + return readUserLP(id, fis); + } catch (IOException ioe) { + Slog.e(LOG_TAG, "Error reading user list"); + } catch (XmlPullParserException pe) { + Slog.e(LOG_TAG, "Error reading user list"); + } finally { + IoUtils.closeQuietly(fis); + } + return null; + } + + @VisibleForTesting + UserData readUserLP(int id, InputStream is) throws IOException, + XmlPullParserException { int flags = 0; int serialNumber = id; String name = null; @@ -2029,6 +2078,7 @@ public class UserManagerService extends IUserManager.Stub { long lastLoggedInTime = 0L; String lastLoggedInFingerprint = null; int profileGroupId = UserInfo.NO_PROFILE_GROUP_ID; + int profileBadge = 0; int restrictedProfileParentId = UserInfo.NO_PROFILE_GROUP_ID; boolean partial = false; boolean guestToRemove = false; @@ -2039,120 +2089,106 @@ public class UserManagerService extends IUserManager.Stub { Bundle baseRestrictions = new Bundle(); Bundle localRestrictions = new Bundle(); - FileInputStream fis = null; - try { - AtomicFile userFile = - new AtomicFile(new File(mUsersDir, Integer.toString(id) + XML_SUFFIX)); - fis = userFile.openRead(); - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(fis, StandardCharsets.UTF_8.name()); - int type; - while ((type = parser.next()) != XmlPullParser.START_TAG - && type != XmlPullParser.END_DOCUMENT) { - // Skip - } + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(is, StandardCharsets.UTF_8.name()); + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + // Skip + } - if (type != XmlPullParser.START_TAG) { - Slog.e(LOG_TAG, "Unable to read user " + id); + if (type != XmlPullParser.START_TAG) { + Slog.e(LOG_TAG, "Unable to read user " + id); + return null; + } + + if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) { + int storedId = readIntAttribute(parser, ATTR_ID, -1); + if (storedId != id) { + Slog.e(LOG_TAG, "User id does not match the file name"); return null; } + serialNumber = readIntAttribute(parser, ATTR_SERIAL_NO, id); + flags = readIntAttribute(parser, ATTR_FLAGS, 0); + iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH); + creationTime = readLongAttribute(parser, ATTR_CREATION_TIME, 0); + lastLoggedInTime = readLongAttribute(parser, ATTR_LAST_LOGGED_IN_TIME, 0); + lastLoggedInFingerprint = parser.getAttributeValue(null, + ATTR_LAST_LOGGED_IN_FINGERPRINT); + profileGroupId = readIntAttribute(parser, ATTR_PROFILE_GROUP_ID, + UserInfo.NO_PROFILE_GROUP_ID); + profileBadge = readIntAttribute(parser, ATTR_PROFILE_BADGE, 0); + restrictedProfileParentId = readIntAttribute(parser, + ATTR_RESTRICTED_PROFILE_PARENT_ID, UserInfo.NO_PROFILE_GROUP_ID); + String valueString = parser.getAttributeValue(null, ATTR_PARTIAL); + if ("true".equals(valueString)) { + partial = true; + } + valueString = parser.getAttributeValue(null, ATTR_GUEST_TO_REMOVE); + if ("true".equals(valueString)) { + guestToRemove = true; + } - if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) { - int storedId = readIntAttribute(parser, ATTR_ID, -1); - if (storedId != id) { - Slog.e(LOG_TAG, "User id does not match the file name"); - return null; - } - serialNumber = readIntAttribute(parser, ATTR_SERIAL_NO, id); - flags = readIntAttribute(parser, ATTR_FLAGS, 0); - iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH); - creationTime = readLongAttribute(parser, ATTR_CREATION_TIME, 0); - lastLoggedInTime = readLongAttribute(parser, ATTR_LAST_LOGGED_IN_TIME, 0); - lastLoggedInFingerprint = parser.getAttributeValue(null, - ATTR_LAST_LOGGED_IN_FINGERPRINT); - profileGroupId = readIntAttribute(parser, ATTR_PROFILE_GROUP_ID, - UserInfo.NO_PROFILE_GROUP_ID); - restrictedProfileParentId = readIntAttribute(parser, - ATTR_RESTRICTED_PROFILE_PARENT_ID, UserInfo.NO_PROFILE_GROUP_ID); - String valueString = parser.getAttributeValue(null, ATTR_PARTIAL); - if ("true".equals(valueString)) { - partial = true; - } - valueString = parser.getAttributeValue(null, ATTR_GUEST_TO_REMOVE); - if ("true".equals(valueString)) { - guestToRemove = true; - } + seedAccountName = parser.getAttributeValue(null, ATTR_SEED_ACCOUNT_NAME); + seedAccountType = parser.getAttributeValue(null, ATTR_SEED_ACCOUNT_TYPE); + if (seedAccountName != null || seedAccountType != null) { + persistSeedData = true; + } - seedAccountName = parser.getAttributeValue(null, ATTR_SEED_ACCOUNT_NAME); - seedAccountType = parser.getAttributeValue(null, ATTR_SEED_ACCOUNT_TYPE); - if (seedAccountName != null || seedAccountType != null) { + int outerDepth = parser.getDepth(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + String tag = parser.getName(); + if (TAG_NAME.equals(tag)) { + type = parser.next(); + if (type == XmlPullParser.TEXT) { + name = parser.getText(); + } + } else if (TAG_RESTRICTIONS.equals(tag)) { + UserRestrictionsUtils.readRestrictions(parser, baseRestrictions); + } else if (TAG_DEVICE_POLICY_RESTRICTIONS.equals(tag)) { + UserRestrictionsUtils.readRestrictions(parser, localRestrictions); + } else if (TAG_ACCOUNT.equals(tag)) { + type = parser.next(); + if (type == XmlPullParser.TEXT) { + account = parser.getText(); + } + } else if (TAG_SEED_ACCOUNT_OPTIONS.equals(tag)) { + seedAccountOptions = PersistableBundle.restoreFromXml(parser); persistSeedData = true; } - - int outerDepth = parser.getDepth(); - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - String tag = parser.getName(); - if (TAG_NAME.equals(tag)) { - type = parser.next(); - if (type == XmlPullParser.TEXT) { - name = parser.getText(); - } - } else if (TAG_RESTRICTIONS.equals(tag)) { - UserRestrictionsUtils.readRestrictions(parser, baseRestrictions); - } else if (TAG_DEVICE_POLICY_RESTRICTIONS.equals(tag)) { - UserRestrictionsUtils.readRestrictions(parser, localRestrictions); - } else if (TAG_ACCOUNT.equals(tag)) { - type = parser.next(); - if (type == XmlPullParser.TEXT) { - account = parser.getText(); - } - } else if (TAG_SEED_ACCOUNT_OPTIONS.equals(tag)) { - seedAccountOptions = PersistableBundle.restoreFromXml(parser); - persistSeedData = true; - } - } - } - - // Create the UserInfo object that gets passed around - UserInfo userInfo = new UserInfo(id, name, iconPath, flags); - userInfo.serialNumber = serialNumber; - userInfo.creationTime = creationTime; - userInfo.lastLoggedInTime = lastLoggedInTime; - userInfo.lastLoggedInFingerprint = lastLoggedInFingerprint; - userInfo.partial = partial; - userInfo.guestToRemove = guestToRemove; - userInfo.profileGroupId = profileGroupId; - userInfo.restrictedProfileParentId = restrictedProfileParentId; - - // Create the UserData object that's internal to this class - UserData userData = new UserData(); - userData.info = userInfo; - userData.account = account; - userData.seedAccountName = seedAccountName; - userData.seedAccountType = seedAccountType; - userData.persistSeedData = persistSeedData; - userData.seedAccountOptions = seedAccountOptions; - - synchronized (mRestrictionsLock) { - mBaseUserRestrictions.put(id, baseRestrictions); - mDevicePolicyLocalUserRestrictions.put(id, localRestrictions); - } - return userData; - } catch (IOException ioe) { - } catch (XmlPullParserException pe) { - } finally { - if (fis != null) { - try { - fis.close(); - } catch (IOException e) { - } } } - return null; + + // Create the UserInfo object that gets passed around + UserInfo userInfo = new UserInfo(id, name, iconPath, flags); + userInfo.serialNumber = serialNumber; + userInfo.creationTime = creationTime; + userInfo.lastLoggedInTime = lastLoggedInTime; + userInfo.lastLoggedInFingerprint = lastLoggedInFingerprint; + userInfo.partial = partial; + userInfo.guestToRemove = guestToRemove; + userInfo.profileGroupId = profileGroupId; + userInfo.profileBadge = profileBadge; + userInfo.restrictedProfileParentId = restrictedProfileParentId; + + // Create the UserData object that's internal to this class + UserData userData = new UserData(); + userData.info = userInfo; + userData.account = account; + userData.seedAccountName = seedAccountName; + userData.seedAccountType = seedAccountType; + userData.persistSeedData = persistSeedData; + userData.seedAccountOptions = seedAccountOptions; + + synchronized (mRestrictionsLock) { + mBaseUserRestrictions.put(id, baseRestrictions); + mDevicePolicyLocalUserRestrictions.put(id, localRestrictions); + } + return userData; } private int readIntAttribute(XmlPullParser parser, String attr, int defaultValue) { @@ -2316,6 +2352,9 @@ public class UserManagerService extends IUserManager.Stub { userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0; userInfo.partial = true; userInfo.lastLoggedInFingerprint = Build.FINGERPRINT; + if (isManagedProfile && parentId != UserHandle.USER_NULL) { + userInfo.profileBadge = getFreeProfileBadgeLU(parentId); + } userData = new UserData(); userData.info = userInfo; mUsers.put(userId, userData); @@ -3643,4 +3682,39 @@ public class UserManagerService extends IUserManager.Stub { Log.d(LOG_TAG, message + (DBG_WITH_STACKTRACE ? " called at\n" + Debug.getCallers(10, " ") : "")); } + + @VisibleForTesting + static int getMaxManagedProfiles() { + // Allow overriding max managed profiles on debuggable builds for testing + // of multiple profiles. + if (!Build.IS_DEBUGGABLE) { + return MAX_MANAGED_PROFILES; + } else { + return SystemProperties.getInt("persist.sys.max_profiles", + MAX_MANAGED_PROFILES); + } + } + + @VisibleForTesting + int getFreeProfileBadgeLU(int parentUserId) { + int maxManagedProfiles = getMaxManagedProfiles(); + boolean[] usedBadges = new boolean[maxManagedProfiles]; + final int userSize = mUsers.size(); + for (int i = 0; i < userSize; i++) { + UserInfo ui = mUsers.valueAt(i).info; + // Check which badge indexes are already used by this profile group. + if (ui.isManagedProfile() + && ui.profileGroupId == parentUserId + && !mRemovingUserIds.get(ui.id) + && ui.profileBadge < maxManagedProfiles) { + usedBadges[ui.profileBadge] = true; + } + } + for (int i = 0; i < maxManagedProfiles; i++) { + if (!usedBadges[i]) { + return i; + } + } + return 0; + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java new file mode 100644 index 0000000000000..ad514cfbe464f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.pm; + +import android.app.ApplicationPackageManager; +import android.content.pm.UserInfo; +import android.os.Looper; +import android.os.UserManagerInternal; +import android.os.UserHandle; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import android.support.test.filters.MediumTest; + +import com.android.server.LocalServices; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + *

Run with:

+ * runtest -c com.android.server.pm.UserManagerServiceCreateProfileTest frameworks-services
+ * 
+ */ +@RunWith(AndroidJUnit4.class) +@MediumTest +public class UserManagerServiceCreateProfileTest { + private UserManagerService mUserManagerService; + + @Before + public void setup() { + // Currently UserManagerService cannot be instantiated twice inside a VM without a cleanup + // TODO: Remove once UMS supports proper dependency injection + if (Looper.myLooper() == null) { + Looper.prepare(); + } + LocalServices.removeServiceForTest(UserManagerInternal.class); + mUserManagerService = new UserManagerService(InstrumentationRegistry.getContext()); + + // The tests assume that the device has one user and its the system user. + List users = mUserManagerService.getUsers(/* excludeDying */ false); + assertEquals("Multiple users so this test can't run.", 1, users.size()); + assertEquals("Only user present isn't the system user.", + UserHandle.USER_SYSTEM, users.get(0).id); + } + + @Test + public void testGetProfiles() { + try { + // Pretend we have a secondary user with a profile. + UserInfo secondaryUser = addUser(); + UserInfo profile = addProfile(secondaryUser); + + // System user should still have no profile so getProfiles should just return 1 user. + List users = + mUserManagerService.getProfiles(UserHandle.USER_SYSTEM, /*excludeDying*/ false); + assertEquals("Profiles returned where none should exist", 1, users.size()); + assertEquals("Missing system user from profile list of system user", + UserHandle.USER_SYSTEM, users.get(0).id); + + // Secondary user should have 1 profile, so return that and itself. + users = mUserManagerService.getProfiles(secondaryUser.id, /*excludeDying*/ false); + assertEquals("Profiles returned where none should exist", 2, users.size()); + assertTrue("Missing secondary user id", users.get(0).id == secondaryUser.id + || users.get(1).id == secondaryUser.id); + assertTrue("Missing profile user id", users.get(0).id == profile.id + || users.get(1).id == profile.id); + } finally { + removeUsers(); + } + } + + @Test + public void testProfileBadge() { + try { + // First profile for system user should get badge 0 + assertEquals("First profile isn't given badge index 0", 0, + mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM)); + + // Pretend we have a secondary user. + UserInfo secondaryUser = addUser(); + + // Check first profile badge for secondary user is also 0. + assertEquals("First profile for secondary user isn't given badge index 0", 0, + mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id)); + + // Shouldn't impact the badge for profile in system user + assertEquals("First profile isn't given badge index 0 with secondary user", 0, + mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM)); + + // Pretend a secondary user has a profile. + addProfile(secondaryUser); + + // Shouldn't have impacted the badge for the system user + assertEquals("First profile isn't given badge index 0 in secondary user", 0, + mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM)); + } finally { + removeUsers(); + } + } + + @Test + public void testProfileBadgeUnique() { + try { + List users = mUserManagerService.getUsers(/* excludeDying */ false); + UserInfo system = users.get(0); + // Badges should get allocated 0 -> max + for (int i = 0; i < UserManagerService.getMaxManagedProfiles(); ++i) { + int nextBadge = mUserManagerService.getFreeProfileBadgeLU(UserHandle.USER_SYSTEM); + assertEquals("Wrong badge allocated", i, nextBadge); + UserInfo profile = addProfile(system); + profile.profileBadge = nextBadge; + } + } finally { + removeUsers(); + } + } + + @Test + public void testProfileBadgeReuse() { + try { + // Pretend we have a secondary user with a profile. + UserInfo secondaryUser = addUser(); + UserInfo profile = addProfile(secondaryUser); + // Add the profile it to the users being removed. + mUserManagerService.addRemovingUserIdLocked(profile.id); + // We should reuse the badge from the profile being removed. + assertEquals("Badge index not reused while removing a user", 0, + mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id)); + + // Edge case of reuse that only applies if we ever support 3 managed profiles + // We should prioritise using lower badge indexes + if (UserManagerService.getMaxManagedProfiles() > 2) { + UserInfo profileBadgeOne = addProfile(secondaryUser); + profileBadgeOne.profileBadge = 1; + // 0 and 2 are free, we should reuse 0 rather than 2. + assertEquals("Lower index not used", 0, + mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id)); + } + } finally { + removeUsers(); + } + } + + @Test + public void testNumberOfBadges() { + assertTrue("Max profiles greater than number of badges", + UserManagerService.MAX_MANAGED_PROFILES + <= ApplicationPackageManager.CORP_BADGE_COLORS.length); + assertEquals("Num colors doesn't match number of badge labels", + ApplicationPackageManager.CORP_BADGE_COLORS.length, + ApplicationPackageManager.CORP_BADGE_LABEL_RES_ID.length); + } + + private void removeUsers() { + List users = mUserManagerService.getUsers(/* excludeDying */ false); + for (UserInfo user: users) { + if (user.id != UserHandle.USER_SYSTEM) { + mUserManagerService.removeUserInfo(user.id); + } + } + } + + private UserInfo addProfile(UserInfo user) { + user.profileGroupId = user.id; + UserInfo profile = new UserInfo( + mUserManagerService.getNextAvailableId(), "profile", + UserInfo.FLAG_MANAGED_PROFILE); + profile.profileGroupId = user.id; + mUserManagerService.putUserInfo(profile); + return profile; + } + + private UserInfo addUser() { + UserInfo secondaryUser = new UserInfo( + mUserManagerService.getNextAvailableId(), "secondary", /* flags */ 0); + mUserManagerService.putUserInfo(secondaryUser); + return secondaryUser; + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java new file mode 100644 index 0000000000000..575d7a34ecbe0 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.pm; + +import android.content.pm.UserInfo; +import android.os.Looper; +import android.os.Parcel; +import android.os.UserManagerInternal; +import android.os.UserHandle; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import android.support.test.filters.MediumTest; + +import com.android.server.LocalServices; +import com.android.server.pm.UserManagerService.UserData; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + *

Run with:

+ * runtest -c com.android.server.pm.UserManagerServiceUserInfoTest frameworks-services
+ * 
+ */ +@RunWith(AndroidJUnit4.class) +@MediumTest +public class UserManagerServiceUserInfoTest { + private UserManagerService mUserManagerService; + + @Before + public void setup() { + // Currently UserManagerService cannot be instantiated twice inside a VM without a cleanup + // TODO: Remove once UMS supports proper dependency injection + if (Looper.myLooper() == null) { + Looper.prepare(); + } + LocalServices.removeServiceForTest(UserManagerInternal.class); + mUserManagerService = new UserManagerService(InstrumentationRegistry.getContext()); + + // The tests assume that the device has one user and its the system user. + List users = mUserManagerService.getUsers(/* excludeDying */ false); + assertEquals("Multiple users so this test can't run.", 1, users.size()); + assertEquals("Only user present isn't the system user.", + UserHandle.USER_SYSTEM, users.get(0).id); + } + + @Test + public void testWriteReadUserInfo() throws Exception { + UserData data = new UserData(); + data.info = createUser(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(baos); + mUserManagerService.writeUserLP(data, out); + byte[] bytes = baos.toByteArray(); + + UserData read = mUserManagerService.readUserLP( + data.info.id, new ByteArrayInputStream(bytes)); + + assertUserInfoEquals(data.info, read.info); + } + + @Test + public void testParcelUnparcelUserInfo() throws Exception { + UserInfo info = createUser(); + + Parcel out = Parcel.obtain(); + info.writeToParcel(out, 0); + byte[] data = out.marshall(); + out.recycle(); + + Parcel in = Parcel.obtain(); + in.unmarshall(data, 0, data.length); + in.setDataPosition(0); + UserInfo read = UserInfo.CREATOR.createFromParcel(in); + in.recycle(); + + assertUserInfoEquals(info, read); + } + + private UserInfo createUser() { + UserInfo user = new UserInfo(/*id*/ 21, "A Name", "A path", /*flags*/ 0x0ff0ff); + user.serialNumber = 5; + user.creationTime = 4L << 32; + user.lastLoggedInTime = 5L << 32; + user.lastLoggedInFingerprint = "afingerprint"; + user.profileGroupId = 45; + user.restrictedProfileParentId = 4; + user.profileBadge = 2; + user.partial = true; + user.guestToRemove = true; + return user; + } + + private void assertUserInfoEquals(UserInfo one, UserInfo two) { + assertEquals("Id not preserved", one.id, two.id); + assertEquals("Name not preserved", one.name, two.name); + assertEquals("Icon path not preserved", one.iconPath, two.iconPath); + assertEquals("Flags not preserved", one.flags, two.flags); + assertEquals("profile group not preserved", one.profileGroupId, + two.profileGroupId); + assertEquals("restricted profile parent not preseved", one.restrictedProfileParentId, + two.restrictedProfileParentId); + assertEquals("profile badge not preseved", one.profileBadge, two.profileBadge); + assertEquals("partial not preseved", one.partial, two.partial); + assertEquals("guestToRemove not preseved", one.guestToRemove, two.guestToRemove); + } +} diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index fee3aa508810b..1bd5b1dc98c27 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -524,14 +524,6 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } - /** @hide */ - @Override - public Drawable getManagedUserBadgedDrawable(Drawable drawable, Rect badgeLocation, - int badgeDensity) { - throw new UnsupportedOperationException(); - } - - @Override public Drawable getUserBadgedIcon(Drawable icon, UserHandle user) { throw new UnsupportedOperationException(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java index 0b169bd474c6b..f47b1050ca73b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java @@ -494,12 +494,6 @@ public class BridgePackageManager extends PackageManager { return null; } - @Override - public Drawable getManagedUserBadgedDrawable(Drawable drawable, Rect badgeLocation, - int badgeDensity) { - return null; - } - @Override public Drawable getUserBadgedIcon(Drawable icon, UserHandle user) { return null;