Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d15b2f72 authored by Bookatz's avatar Bookatz
Browse files

Allow setting User Restrictions per user type

FULL and PROFILE user types now have their default restrictions
delineated in UserTypeFactory. The restrictions for these user types can
be customized in config_user_types, along with the other user type
customization.

Note: even though FULL user types can now be customized, the only
supported customization is user restrictions. Moreover, new FULL user
types cannot be defined.

This cl also has the following repercussions:
-When a user is created (but not yet started), the applied restrictions
are not propagated as an update (prior to this, the restrictions for new
users - other than guest - were set via setUserRestriction, which did
more than just store the data). When the user is started, the
restrictions will still be propagated.

-When a user is created via adb, its default restrictions are applied.
Previously this was not the case except for guest users.

-When an Admin user is created/set, it is now the callers responsibility to
adjust its restrictions. UserManager will no longer automatically remove
the DISALLOW_SMS/DISALLOW_OUTGOING_CALLS restrictions for Admins.

Also note that Restricted Users have additional hardcoded restrictions,
indepent of this cl.

Test: atest UserManagerServiceUserTypeTest
Test: atest UserManagerTest
Bug: 142482943
Bug: 143491938

Change-Id: Idfec610da871aea2bc7109d11d1eb9a957e080a4
parent 95f02701
Loading
Loading
Loading
Loading
+3 −12
Original line number Diff line number Diff line
@@ -2391,19 +2391,11 @@ public class UserManager {
     */
    public @Nullable UserInfo createUser(@Nullable String name, @NonNull String userType,
            @UserInfoFlag int flags) {
        UserInfo user = null;
        try {
            user = mService.createUser(name, userType, flags);
            // TODO: Keep this in sync with
            // UserManagerService.LocalService.createUserEvenWhenDisallowed
            if (user != null && !user.isAdmin() && !user.isDemo()) {
                mService.setUserRestriction(DISALLOW_SMS, true, user.id);
                mService.setUserRestriction(DISALLOW_OUTGOING_CALLS, true, user.id);
            }
            return mService.createUser(name, userType, flags);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
        return user;
    }

    /**
@@ -2748,8 +2740,7 @@ public class UserManager {
    /**
     * Assigns admin privileges to the user, if such a user exists.
     *
     * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} and
     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permissions.
     * <p>Note that this does not alter the user's pre-existing user restrictions.
     *
     * @param userId the id of the user to become admin
     * @hide
+38 −5
Original line number Diff line number Diff line
@@ -18,14 +18,33 @@
This xml file allows customization of Android multiuser user types.
It is parsed by frameworks/base/services/core/java/com/android/server/pm/UserTypeFactory.java.

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++   IMPORTANT NOTE   ++++++++++++++++++++++++++++++++++++++++
Although device customization is possible here, it is largely untested.
In particular, although this file allows new profile types to be created, and allows modifying the
number of managed profiles allowed on the device, the consequences of doing so is untested.
OEMs are advised to test very carefully any significant customization.
Further support is planned for later releases.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Pre-defined (AOSP) user types can be customized and new types can be defined. The syntax is the
same in both cases.
Currently, only profiles (not full or system users) can be customized/defined.

The following example modifies an AOSP user type (android.os.usertype.profile.MANAGED) and creates
a new user type (com.example.profilename):
Currently, only profiles (not full or system users) can be freely customized/defined.
Full users (i.e. non-system, non-profile) users cannot be defined, and the only property of them
that can be customized are the default-restrictions.
System users cannot be customized here; their default-restrictions must be set using
com.android.internal.R.array.config_defaultFirstUserRestrictions.

The following example modifies two AOSP user types (the FULL user android.os.usertype.full.SECONDARY
and the PROFILE user android.os.usertype.profile.MANAGED) and creates a new PROFILE user type
(com.example.profilename):

<user-types>
    <full-type name="android.os.usertype.full.SECONDARY" >
        <default-restrictions no_sms="true" />
    </full-type>

    <profile-type
        name='android.os.usertype.profile.MANAGED'
        max-allowed-per-parent='2'
@@ -40,17 +59,21 @@ a new user type (com.example.profilename):
            <item res='@android:color/profile_badge_1' />
            <item res='@android:color/profile_badge_2' />
        </badge-colors>
        <default-restrictions no_sms="true" no_outgoing_calls="true" />
    </profile-type>

    <profile-type
        name="com.example.profilename"
        max-allowed-per-parent="2" \>
        max-allowed-per-parent="2" />
</user-types>

Mandatory attributes:
    name

Supported optional properties (to be used as shown in the example above):
Supported optional properties (to be used as shown in the example above) are as follows.
For profile and full users:
    default-restrictions (with values defined in UserRestrictionUtils.USER_RESTRICTIONS)
For profile users only:
    max-allowed-per-parent
    icon-badge
    badge-plain
@@ -60,6 +83,16 @@ Supported optional properties (to be used as shown in the example above):

See UserTypeFactory.java and UserTypeDetails.java for the meaning (and default values) of these
fields.

Any property that is specified overwrites the AOSP default. For example, if there is no
default-restrictions element, then the AOSP defaults for that user type will be used; however, if
there is a default-restrictions element, then the AOSP default restrictions will be completely
ignored and will instead obey this configuration.

If this file is updated, the properties of any pre-existing user types will be updated too.
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.

-->
<user-types>
</user-types>
+17 −26
Original line number Diff line number Diff line
@@ -579,15 +579,6 @@ public class UserManagerService extends IUserManager.Stub {
            applyUserRestrictionsLR(UserHandle.USER_SYSTEM);
        }

        UserInfo currentGuestUser = findCurrentGuestUser();
        if (currentGuestUser != null && !hasUserRestriction(
                UserManager.DISALLOW_CONFIG_WIFI, currentGuestUser.id)) {
            // If a guest user currently exists, apply the DISALLOW_CONFIG_WIFI option
            // to it, in case this guest was created in a previous version where this
            // user restriction was not a default guest restriction.
            setUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, true, currentGuestUser.id);
        }

        mContext.registerReceiver(mDisableQuietModeCallback,
                new IntentFilter(ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK),
                null, mHandler);
@@ -1118,11 +1109,6 @@ public class UserManagerService extends IUserManager.Stub {
            info.flags ^= UserInfo.FLAG_ADMIN;
            writeUserLP(getUserDataLU(info.id));
        }

        // Remove non-admin restrictions.
        // Keep synchronized with createUserEvenWhenDisallowed.
        setUserRestriction(UserManager.DISALLOW_SMS, false, userId);
        setUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, false, userId);
    }

    /**
@@ -1602,10 +1588,12 @@ public class UserManagerService extends IUserManager.Stub {
    private void initDefaultGuestRestrictions() {
        synchronized (mGuestRestrictions) {
            if (mGuestRestrictions.isEmpty()) {
                mGuestRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_WIFI, true);
                mGuestRestrictions.putBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true);
                mGuestRestrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true);
                mGuestRestrictions.putBoolean(UserManager.DISALLOW_SMS, true);
                UserTypeDetails guestType = mUserTypes.get(UserManager.USER_TYPE_FULL_GUEST);
                if (guestType == null) {
                    Slog.wtf(LOG_TAG, "Can't set default guest restrictions: type doesn't exist.");
                    return;
                }
                guestType.addDefaultRestrictionsTo(mGuestRestrictions);
            }
        }
    }
@@ -2494,6 +2482,12 @@ public class UserManagerService extends IUserManager.Stub {
                        mDevicePolicyLocalUserRestrictions, mDevicePolicyGlobalUserRestrictions
                );
            }
            // DISALLOW_CONFIG_WIFI was made a default guest restriction some time during version 6.
            final UserInfo currentGuestUser = findCurrentGuestUser();
            if (currentGuestUser != null && !hasUserRestriction(
                    UserManager.DISALLOW_CONFIG_WIFI, currentGuestUser.id)) {
                setUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, true, currentGuestUser.id);
            }
            userVersion = 7;
        }

@@ -3245,12 +3239,15 @@ public class UserManagerService extends IUserManager.Stub {
                writeUserLP(userData);
            }
            updateUserIds();

            Bundle restrictions = new Bundle();
            // TODO(b/142482943): Generalize this using UserTypeDetails default restrictions.
            if (isGuest) {
                // Guest default restrictions can be modified via setDefaultGuestRestrictions.
                synchronized (mGuestRestrictions) {
                    restrictions.putAll(mGuestRestrictions);
                }
            } else {
                userTypeDetails.addDefaultRestrictionsTo(restrictions);
            }
            synchronized (mRestrictionsLock) {
                mBaseUserRestrictions.append(userId, restrictions);
@@ -4666,14 +4663,8 @@ public class UserManagerService extends IUserManager.Stub {
        @Override
        public UserInfo createUserEvenWhenDisallowed(String name, @NonNull String userType,
                @UserInfoFlag int flags, String[] disallowedPackages) {
            UserInfo user = createUserInternalUnchecked(name, userType, flags,
            return createUserInternalUnchecked(name, userType, flags,
                    UserHandle.USER_NULL, /* preCreated= */ false, disallowedPackages);
            // Keep this in sync with UserManager.createUser
            if (user != null && !user.isAdmin() && !user.isDemo()) {
                setUserRestriction(UserManager.DISALLOW_SMS, true, user.id);
                setUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, true, user.id);
            }
            return user;
        }

        @Override
+30 −21
Original line number Diff line number Diff line
@@ -19,18 +19,17 @@ package com.android.server.pm;
import android.annotation.ColorRes;
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.content.pm.UserInfo;
import android.content.pm.UserInfo.UserInfoFlag;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.UserManager;

import com.android.internal.util.Preconditions;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Contains the details about a multiuser "user type", such as a
@@ -75,8 +74,13 @@ public final class UserTypeDetails {
    /** The {@link UserInfo.UserInfoFlag}s that all users of this type will automatically have. */
    private final @UserInfoFlag int mDefaultUserInfoPropertyFlags;

    // TODO(b/142482943): Hook these up to something and set them for each type.
    private final List<String> mDefaultRestrictions;
    /**
     * List of User Restrictions to apply by default to newly created users of this type.
     * <p>Does not apply to SYSTEM users (since they are not formally created); for them use
     * {@link com.android.internal.R.array#config_defaultFirstUserRestrictions} instead.
     * The Bundle is of the form used by {@link UserRestrictionsUtils}.
     */
    private final @Nullable Bundle mDefaultRestrictions;


    // Fields for profiles only, controlling the nature of their badges.
@@ -99,7 +103,7 @@ public final class UserTypeDetails {
     *
     * <p>Must be set if mIconBadge is set.
     */
    private final int[] mBadgeLabels;
    private final @Nullable int[] mBadgeLabels;

    /**
     * Resource ID ({@link ColorRes}) of the colors badge put on icons.
@@ -110,22 +114,21 @@ public final class UserTypeDetails {
     *
     * <p>Must be set if mIconBadge is set.
     */
    private final int[] mBadgeColors;
    private final @Nullable int[] mBadgeColors;

    private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed,
            @UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label,
            int maxAllowedPerParent,
            int iconBadge, int badgePlain, int badgeNoBackground,
            int[] badgeLabels, int[] badgeColors,
            ArrayList<String> defaultRestrictions) {
            @Nullable int[] badgeLabels, @Nullable int[] badgeColors,
            @Nullable Bundle defaultRestrictions) {
        this.mName = name;
        this.mEnabled = enabled;
        this.mMaxAllowed = maxAllowed;
        this.mMaxAllowedPerParent = maxAllowedPerParent;
        this.mBaseType = baseType;
        this.mDefaultUserInfoPropertyFlags = defaultUserInfoPropertyFlags;
        this.mDefaultRestrictions =
                Collections.unmodifiableList(new ArrayList<>(defaultRestrictions));
        this.mDefaultRestrictions = defaultRestrictions;

        this.mIconBadge = iconBadge;
        this.mBadgePlain = badgePlain;
@@ -231,9 +234,14 @@ public final class UserTypeDetails {
        return (mBaseType & UserInfo.FLAG_SYSTEM) != 0;
    }

    // TODO(b/142482943): Hook this up and don't return the original.
    public List<String> getDefaultRestrictions() {
        return mDefaultRestrictions;
    /** Returns a Bundle representing the default user restrictions. */
    @NonNull Bundle getDefaultRestrictions() {
        return UserRestrictionsUtils.clone(mDefaultRestrictions);
    }

    /** Adds the default user restrictions to the given bundle of restrictions. */
    public void addDefaultRestrictionsTo(@NonNull Bundle currentRestrictions) {
        UserRestrictionsUtils.merge(currentRestrictions, mDefaultRestrictions);
    }

    /** Dumps details of the UserTypeDetails. Do not parse this. */
@@ -247,7 +255,8 @@ public final class UserTypeDetails {
        pw.print(prefix); pw.print("mDefaultUserInfoFlags: ");
        pw.println(UserInfo.flagsToString(mDefaultUserInfoPropertyFlags));
        pw.print(prefix); pw.print("mLabel: "); pw.println(mLabel);
        pw.print(prefix); pw.print("mDefaultRestrictions: "); pw.println(mDefaultRestrictions);
        pw.print(prefix); pw.println("mDefaultRestrictions: ");
        UserRestrictionsUtils.dumpRestrictions(pw, prefix + "    ", mDefaultRestrictions);
        pw.print(prefix); pw.print("mIconBadge: "); pw.println(mIconBadge);
        pw.print(prefix); pw.print("mBadgePlain: "); pw.println(mBadgePlain);
        pw.print(prefix); pw.print("mBadgeNoBackground: "); pw.println(mBadgeNoBackground);
@@ -265,11 +274,11 @@ public final class UserTypeDetails {
        private int mMaxAllowed = UNLIMITED_NUMBER_OF_USERS;
        private int mMaxAllowedPerParent = UNLIMITED_NUMBER_OF_USERS;
        private int mDefaultUserInfoPropertyFlags = 0;
        private ArrayList<String> mDefaultRestrictions = new ArrayList<>();
        private @Nullable Bundle mDefaultRestrictions = null;
        private boolean mEnabled = true;
        private int mLabel = Resources.ID_NULL;
        private int[] mBadgeLabels = null;
        private int[] mBadgeColors = null;
        private @Nullable int[] mBadgeLabels = null;
        private @Nullable int[] mBadgeColors = null;
        private @DrawableRes int mIconBadge = Resources.ID_NULL;
        private @DrawableRes int mBadgePlain = Resources.ID_NULL;
        private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL;
@@ -334,13 +343,13 @@ public final class UserTypeDetails {
            return this;
        }

        public Builder setDefaultRestrictions(ArrayList<String> restrictions) {
        public Builder setDefaultRestrictions(@Nullable Bundle restrictions) {
            mDefaultRestrictions = restrictions;
            return this;
        }

        public boolean isBaseTypeProfile() {
            return mBaseType == UserInfo.FLAG_PROFILE;
        @UserInfoFlag int getBaseType() {
            return mBaseType;
        }

        public UserTypeDetails createUserTypeDetails() {
+64 −25
Original line number Diff line number Diff line
@@ -34,8 +34,10 @@ import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;

import static com.android.server.pm.UserTypeDetails.UNLIMITED_NUMBER_OF_USERS;

import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.os.UserManager;
import android.util.ArrayMap;
import android.util.Slog;
@@ -80,8 +82,8 @@ public final class UserTypeFactory {
        builders.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted());
        builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless());

        try (XmlResourceParser parser
                     = Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) {
        try (XmlResourceParser parser =
                     Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) {
            customizeBuilders(builders, parser);
        }

@@ -113,7 +115,8 @@ public final class UserTypeFactory {
                .setBadgeColors(
                        com.android.internal.R.color.profile_badge_1,
                        com.android.internal.R.color.profile_badge_2,
                        com.android.internal.R.color.profile_badge_3);
                        com.android.internal.R.color.profile_badge_3)
                .setDefaultRestrictions(null);
    }

    /**
@@ -124,7 +127,8 @@ public final class UserTypeFactory {
        return new UserTypeDetails.Builder()
                .setName(USER_TYPE_FULL_SECONDARY)
                .setBaseType(FLAG_FULL)
                .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS);
                .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS)
                .setDefaultRestrictions(getDefaultSecondaryUserRestrictions());
    }

    /**
@@ -135,13 +139,12 @@ public final class UserTypeFactory {
                .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
        final int flags = FLAG_GUEST | (ephemeralGuests ? FLAG_EPHEMERAL : 0);

        // TODO(b/142482943): Put UMS.initDefaultGuestRestrictions() here; then fetch them from here

        return new UserTypeDetails.Builder()
                .setName(USER_TYPE_FULL_GUEST)
                .setBaseType(FLAG_FULL)
                .setDefaultUserInfoPropertyFlags(flags)
                .setMaxAllowed(1);
                .setMaxAllowed(1)
                .setDefaultRestrictions(getDefaultGuestUserRestrictions());
    }

    /**
@@ -152,7 +155,8 @@ public final class UserTypeFactory {
                .setName(USER_TYPE_FULL_DEMO)
                .setBaseType(FLAG_FULL)
                .setDefaultUserInfoPropertyFlags(FLAG_DEMO)
                .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS);
                .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS)
                .setDefaultRestrictions(null);
    }

    /**
@@ -164,7 +168,9 @@ public final class UserTypeFactory {
                .setName(USER_TYPE_FULL_RESTRICTED)
                .setBaseType(FLAG_FULL)
                .setDefaultUserInfoPropertyFlags(FLAG_RESTRICTED)
                .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS);
                .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS)
                // NB: UserManagerService.createRestrictedProfile() applies hardcoded restrictions.
                .setDefaultRestrictions(null);
    }

    /**
@@ -186,6 +192,21 @@ public final class UserTypeFactory {
                .setBaseType(FLAG_SYSTEM);
    }

    private static Bundle getDefaultSecondaryUserRestrictions() {
        final Bundle restrictions = new Bundle();
        restrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true);
        restrictions.putBoolean(UserManager.DISALLOW_SMS, true);
        return restrictions;
    }

    private static Bundle getDefaultGuestUserRestrictions() {
        // Guest inherits the secondary user's restrictions, plus has some extra ones.
        final Bundle restrictions = getDefaultSecondaryUserRestrictions();
        restrictions.putBoolean(UserManager.DISALLOW_CONFIG_WIFI, true);
        restrictions.putBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true);
        return restrictions;
    }

    /**
     * Reads the given xml parser to obtain device user-type customization, and updates the given
     * map of {@link UserTypeDetails.Builder}s accordingly.
@@ -200,8 +221,13 @@ public final class UserTypeFactory {
            for (XmlUtils.nextElement(parser);
                    parser.getEventType() != XmlResourceParser.END_DOCUMENT;
                    XmlUtils.nextElement(parser)) {
                final boolean isProfile;
                final String elementName = parser.getName();
                if (!"profile-type".equals(elementName)) {
                if ("profile-type".equals(elementName)) {
                    isProfile = true;
                } else if ("full-type".equals(elementName)) {
                    isProfile = false;
                } else {
                    Slog.w(LOG_TAG, "Skipping unknown element " + elementName + " in "
                                + parser.getPositionDescription());
                    XmlUtils.skipCurrentTag(parser);
@@ -210,7 +236,7 @@ public final class UserTypeFactory {

                String typeName = parser.getAttributeValue(null, "name");
                if (typeName == null) {
                    Slog.w(LOG_TAG, "Skipping profile-type with no name in "
                    Slog.w(LOG_TAG, "Skipping user type with no name in "
                            + parser.getPositionDescription());
                    XmlUtils.skipCurrentTag(parser);
                    continue;
@@ -226,32 +252,45 @@ public final class UserTypeFactory {
                        throw new IllegalArgumentException("Illegal custom user type name "
                                + typeName + ": Non-AOSP user types cannot start with 'android.'");
                    }
                    if (!builder.isBaseTypeProfile()) {
                        throw new IllegalArgumentException("Customization of non-profile user type "
                                + "(" + typeName + ") is not currently supported.");
                    final boolean isValid =
                            (isProfile && builder.getBaseType() == UserInfo.FLAG_PROFILE)
                            || (!isProfile && builder.getBaseType() == UserInfo.FLAG_FULL);
                    if (!isValid) {
                        throw new IllegalArgumentException("Wrong base type to customize user type "
                                + "(" + typeName + "), which is type "
                                + UserInfo.flagsToString(builder.getBaseType()));
                    }
                } else {
                    // typeName refers to a new OEM-defined type which we are defining.
                } else if (isProfile) {
                    // typeName refers to a new OEM-defined profile type which we are defining.
                    Slog.i(LOG_TAG, "Creating custom user type " + typeName);
                    builder = new UserTypeDetails.Builder();
                    builder.setName(typeName);
                    builder.setBaseType(FLAG_PROFILE);
                    builders.put(typeName, builder);
                } else {
                    throw new IllegalArgumentException("Creation of non-profile user type "
                            + "(" + typeName + ") is not currently supported.");
                }

                // Process the attributes (other than name).
                setIntAttribute(parser, "max-allowed-per-parent", builder::setMaxAllowedPerParent);
                if (isProfile) {
                    setIntAttribute(parser, "max-allowed-per-parent",
                            builder::setMaxAllowedPerParent);
                    setResAttribute(parser, "icon-badge", builder::setIconBadge);
                    setResAttribute(parser, "badge-plain", builder::setBadgePlain);
                    setResAttribute(parser, "badge-no-background", builder::setBadgeNoBackground);
                }

                // Process child elements.
                final int depth = parser.getDepth();
                while (XmlUtils.nextElementWithin(parser, depth)) {
                    final String childName = parser.getName();
                    if ("badge-labels".equals(childName)) {
                    if ("default-restrictions".equals(childName)) {
                        final Bundle restrictions = UserRestrictionsUtils.readRestrictions(parser);
                        builder.setDefaultRestrictions(restrictions);
                    } else if (isProfile && "badge-labels".equals(childName)) {
                        setResAttributeArray(parser, builder::setBadgeLabels);
                    } else if ("badge-colors".equals(childName)) {
                    } else if (isProfile && "badge-colors".equals(childName)) {
                        setResAttributeArray(parser, builder::setBadgeColors);
                    } else {
                        Slog.w(LOG_TAG, "Unrecognized tag " + childName + " in "
@@ -282,8 +321,8 @@ public final class UserTypeFactory {
        try {
            fcn.accept(Integer.parseInt(intValue));
        } catch (NumberFormatException e) {
            Slog.e(LOG_TAG, "Cannot parse value of '" + intValue + "' for " + attributeName +
                    " in " + parser.getPositionDescription(), e);
            Slog.e(LOG_TAG, "Cannot parse value of '" + intValue + "' for " + attributeName
                    + " in " + parser.getPositionDescription(), e);
            throw e;
        }
    }
@@ -308,7 +347,7 @@ public final class UserTypeFactory {

    /**
     * Gets the resIds stored in "item" elements (in their "res" attribute) at the current depth.
     * Then performs the performs the given fcn using the int[] array of these resIds.
     * Then performs the given fcn using the int[] array of these resIds.
     * <p>
     * Each xml element is expected to be of the form {@code <item res="someResValue" />}.
     *
Loading