1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.pm;
18 
19 import android.annotation.ColorRes;
20 import android.annotation.DrawableRes;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.StringRes;
24 import android.content.pm.UserInfo;
25 import android.content.pm.UserInfo.UserInfoFlag;
26 import android.content.res.Resources;
27 import android.os.Bundle;
28 import android.os.UserManager;
29 
30 import com.android.internal.util.Preconditions;
31 
32 import java.io.PrintWriter;
33 
34 /**
35  * Contains the details about a multiuser "user type", such as a
36  * {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
37  *
38  * Tests are located in UserManagerServiceUserTypeTest.java.
39  * @hide
40  */
41 public final class UserTypeDetails {
42 
43     /** Indicates that there is no limit to the number of users allowed. */
44     public static final int UNLIMITED_NUMBER_OF_USERS = -1;
45 
46     /** Name of the user type, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}. */
47     private final @NonNull String mName;
48 
49     // TODO(b/142482943): Currently unused. Hook this up.
50     private final boolean mEnabled;
51 
52     // TODO(b/142482943): Currently unused and not set. Hook this up.
53     private final int mLabel;
54 
55     /**
56      * Maximum number of this user type allowed on the device.
57      * Use {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit.
58      */
59     private final int mMaxAllowed;
60 
61     /**
62      * Maximum number of this user type allowed per parent (for user types, like profiles, that
63      * have parents).
64      * Use {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit.
65      */
66     // TODO(b/142482943): Should this also apply to restricted profiles?
67     private final int mMaxAllowedPerParent;
68 
69     // TODO(b/143784345): Update doc when we clean up UserInfo.
70     /** The {@link UserInfo.UserInfoFlag} representing the base type of this user. */
71     private final @UserInfoFlag int mBaseType;
72 
73     // TODO(b/143784345): Update doc/name when we clean up UserInfo.
74     /** The {@link UserInfo.UserInfoFlag}s that all users of this type will automatically have. */
75     private final @UserInfoFlag int mDefaultUserInfoPropertyFlags;
76 
77     /**
78      * List of User Restrictions to apply by default to newly created users of this type.
79      * <p>Does not apply to SYSTEM users (since they are not formally created); for them use
80      * {@link com.android.internal.R.array#config_defaultFirstUserRestrictions} instead.
81      * The Bundle is of the form used by {@link UserRestrictionsUtils}.
82      */
83     private final @Nullable Bundle mDefaultRestrictions;
84 
85 
86     // Fields for profiles only, controlling the nature of their badges.
87     // All badge information should be set if {@link #hasBadge()} is true.
88 
89     /** Resource ID of the badge put on icons. */
90     private @DrawableRes final int mIconBadge;
91     /** Resource ID of the badge. Should be set if mIconBadge is set. */
92     private @DrawableRes final int mBadgePlain;
93     /** Resource ID of the badge without a background. Should be set if mIconBadge is set. */
94     private @DrawableRes final int mBadgeNoBackground;
95 
96     /**
97      * Resource ID ({@link StringRes}) of the of the labels to describe badged apps; should be the
98      * same format as com.android.internal.R.color.profile_badge_1. These are used for accessibility
99      * services.
100      *
101      * <p>This is an array because, in general, there may be multiple users of the same user type.
102      * In this case, the user is indexed according to its {@link UserInfo#profileBadge}.
103      *
104      * <p>Must be set if mIconBadge is set.
105      */
106     private final @Nullable int[] mBadgeLabels;
107 
108     /**
109      * Resource ID ({@link ColorRes}) of the colors badge put on icons.
110      * (The value is a resource ID referring to the color; it is not the color value itself).
111      *
112      * <p>This is an array because, in general, there may be multiple users of the same user type.
113      * In this case, the user is indexed according to its {@link UserInfo#profileBadge}.
114      *
115      * <p>Must be set if mIconBadge is set.
116      */
117     private final @Nullable int[] mBadgeColors;
118 
119     /**
120      * Resource ID ({@link ColorRes}) of the colors badge put on icons when in dark theme.
121      * (The value is a resource ID referring to the color; it is not the color value itself).
122      *
123      * <p>This is an array because, in general, there may be multiple users of the same user type.
124      * In this case, the user is indexed according to its {@link UserInfo#profileBadge}.
125      *
126      * <p>Must be set if mIconBadge is set.
127      */
128     private final @Nullable int[] mDarkThemeBadgeColors;
129 
UserTypeDetails(@onNull String name, boolean enabled, int maxAllowed, @UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label, int maxAllowedPerParent, int iconBadge, int badgePlain, int badgeNoBackground, @Nullable int[] badgeLabels, @Nullable int[] badgeColors, @Nullable int[] darkThemeBadgeColors, @Nullable Bundle defaultRestrictions)130     private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed,
131             @UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label,
132             int maxAllowedPerParent,
133             int iconBadge, int badgePlain, int badgeNoBackground,
134             @Nullable int[] badgeLabels, @Nullable int[] badgeColors,
135             @Nullable int[] darkThemeBadgeColors,
136             @Nullable Bundle defaultRestrictions) {
137         this.mName = name;
138         this.mEnabled = enabled;
139         this.mMaxAllowed = maxAllowed;
140         this.mMaxAllowedPerParent = maxAllowedPerParent;
141         this.mBaseType = baseType;
142         this.mDefaultUserInfoPropertyFlags = defaultUserInfoPropertyFlags;
143         this.mDefaultRestrictions = defaultRestrictions;
144 
145         this.mIconBadge = iconBadge;
146         this.mBadgePlain = badgePlain;
147         this.mBadgeNoBackground = badgeNoBackground;
148         this.mLabel = label;
149         this.mBadgeLabels = badgeLabels;
150         this.mBadgeColors = badgeColors;
151         this.mDarkThemeBadgeColors = darkThemeBadgeColors;
152     }
153 
154     /**
155      * Returns the name of the user type, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
156      */
getName()157     public String getName() {
158         return mName;
159     }
160 
161     // TODO(b/142482943) Hook this up or delete it.
isEnabled()162     public boolean isEnabled() {
163         return mEnabled;
164     }
165 
166     /**
167      * Returns the maximum number of this user type allowed on the device.
168      * <p>Returns {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit.
169      */
getMaxAllowed()170     public int getMaxAllowed() {
171         return mMaxAllowed;
172     }
173 
174     /**
175      * Returns the maximum number of this user type allowed per parent (for user types, like
176      * profiles, that have parents).
177      * <p>Returns {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit.
178      */
getMaxAllowedPerParent()179     public int getMaxAllowedPerParent() {
180         return mMaxAllowedPerParent;
181     }
182 
183     // TODO(b/143784345): Update comment when UserInfo is reorganized.
184     /** The {@link UserInfo.UserInfoFlag}s that all users of this type will automatically have. */
getDefaultUserInfoFlags()185     public int getDefaultUserInfoFlags() {
186         return mDefaultUserInfoPropertyFlags | mBaseType;
187     }
188 
189     // TODO(b/142482943) Hook this up; it is currently unused.
getLabel()190     public int getLabel() {
191         return mLabel;
192     }
193 
194     /** Returns whether users of this user type should be badged. */
hasBadge()195     public boolean hasBadge() {
196         return mIconBadge != Resources.ID_NULL;
197     }
198 
199     /** Resource ID of the badge to put on icons. */
getIconBadge()200     public @DrawableRes int getIconBadge() {
201         return mIconBadge;
202     }
203 
204     /** Resource ID of the badge. Used for {@link UserManager#getUserBadgeResId(int)}. */
getBadgePlain()205     public @DrawableRes int getBadgePlain() {
206         return mBadgePlain;
207     }
208 
209     /** Resource ID of the badge without a background. */
getBadgeNoBackground()210     public @DrawableRes int getBadgeNoBackground() {
211         return mBadgeNoBackground;
212     }
213 
214     /**
215      * Returns the Resource ID of the badgeIndexth badge label, where the badgeIndex is expected
216      * to be the {@link UserInfo#profileBadge} of the user.
217      * If badgeIndex exceeds the number of labels, returns the label for the highest index.
218      */
getBadgeLabel(int badgeIndex)219     public @StringRes int getBadgeLabel(int badgeIndex) {
220         if (mBadgeLabels == null || mBadgeLabels.length == 0 || badgeIndex < 0) {
221             return Resources.ID_NULL;
222         }
223         return mBadgeLabels[Math.min(badgeIndex, mBadgeLabels.length - 1)];
224     }
225 
226     /**
227      * Returns the Resource ID of the badgeIndexth badge color, where the badgeIndex is expected
228      * to be the {@link UserInfo#profileBadge} of the user.
229      * If badgeIndex exceeds the number of colors, returns the color for the highest index.
230      */
getBadgeColor(int badgeIndex)231     public @ColorRes int getBadgeColor(int badgeIndex) {
232         if (mBadgeColors == null || mBadgeColors.length == 0 || badgeIndex < 0) {
233             return Resources.ID_NULL;
234         }
235         return mBadgeColors[Math.min(badgeIndex, mBadgeColors.length - 1)];
236     }
237 
238     /**
239      * Returns the Resource ID of the badgeIndexth dark theme badge color, where the badgeIndex is
240      * expected to be the {@link UserInfo#profileBadge} of the user.
241      * If dark theme badge colors haven't been set, use the light theme badge color.
242      * If badgeIndex exceeds the number of colors, returns the color for the highest index.
243      */
getDarkThemeBadgeColor(int badgeIndex)244     public @ColorRes int getDarkThemeBadgeColor(int badgeIndex) {
245         if (mDarkThemeBadgeColors == null || mDarkThemeBadgeColors.length == 0 || badgeIndex < 0) {
246             return getBadgeColor(badgeIndex);
247         }
248         return mDarkThemeBadgeColors[Math.min(badgeIndex, mDarkThemeBadgeColors.length - 1)];
249     }
250 
isProfile()251     public boolean isProfile() {
252         return (mBaseType & UserInfo.FLAG_PROFILE) != 0;
253     }
254 
isFull()255     public boolean isFull() {
256         return (mBaseType & UserInfo.FLAG_FULL) != 0;
257     }
258 
isSystem()259     public boolean isSystem() {
260         return (mBaseType & UserInfo.FLAG_SYSTEM) != 0;
261     }
262 
263     /** Returns a Bundle representing the default user restrictions. */
getDefaultRestrictions()264     @NonNull Bundle getDefaultRestrictions() {
265         return UserRestrictionsUtils.clone(mDefaultRestrictions);
266     }
267 
268     /** Adds the default user restrictions to the given bundle of restrictions. */
addDefaultRestrictionsTo(@onNull Bundle currentRestrictions)269     public void addDefaultRestrictionsTo(@NonNull Bundle currentRestrictions) {
270         UserRestrictionsUtils.merge(currentRestrictions, mDefaultRestrictions);
271     }
272 
273     /** Dumps details of the UserTypeDetails. Do not parse this. */
dump(PrintWriter pw)274     public void dump(PrintWriter pw) {
275         final String prefix = "        ";
276         pw.print(prefix); pw.print("mName: "); pw.println(mName);
277         pw.print(prefix); pw.print("mBaseType: "); pw.println(UserInfo.flagsToString(mBaseType));
278         pw.print(prefix); pw.print("mEnabled: "); pw.println(mEnabled);
279         pw.print(prefix); pw.print("mMaxAllowed: "); pw.println(mMaxAllowed);
280         pw.print(prefix); pw.print("mMaxAllowedPerParent: "); pw.println(mMaxAllowedPerParent);
281         pw.print(prefix); pw.print("mDefaultUserInfoFlags: ");
282         pw.println(UserInfo.flagsToString(mDefaultUserInfoPropertyFlags));
283         pw.print(prefix); pw.print("mLabel: "); pw.println(mLabel);
284 
285         if (isSystem()) {
286             pw.print(prefix); pw.println("config_defaultFirstUserRestrictions: ");
287             try {
288                 final Bundle restrictions = new Bundle();
289                 final String[] defaultFirstUserRestrictions = Resources.getSystem().getStringArray(
290                         com.android.internal.R.array.config_defaultFirstUserRestrictions);
291                 for (String userRestriction : defaultFirstUserRestrictions) {
292                     if (UserRestrictionsUtils.isValidRestriction(userRestriction)) {
293                         restrictions.putBoolean(userRestriction, true);
294                     }
295                 }
296                 UserRestrictionsUtils.dumpRestrictions(pw, prefix + "    ", restrictions);
297             } catch (Resources.NotFoundException e) {
298                 pw.print(prefix); pw.println("    none - resource not found");
299             }
300         } else {
301             pw.print(prefix); pw.println("mDefaultRestrictions: ");
302             UserRestrictionsUtils.dumpRestrictions(pw, prefix + "    ", mDefaultRestrictions);
303         }
304 
305         pw.print(prefix); pw.print("mIconBadge: "); pw.println(mIconBadge);
306         pw.print(prefix); pw.print("mBadgePlain: "); pw.println(mBadgePlain);
307         pw.print(prefix); pw.print("mBadgeNoBackground: "); pw.println(mBadgeNoBackground);
308         pw.print(prefix); pw.print("mBadgeLabels.length: ");
309         pw.println(mBadgeLabels != null ? mBadgeLabels.length : "0(null)");
310         pw.print(prefix); pw.print("mBadgeColors.length: ");
311         pw.println(mBadgeColors != null ? mBadgeColors.length : "0(null)");
312         pw.print(prefix); pw.print("mDarkThemeBadgeColors.length: ");
313         pw.println(mDarkThemeBadgeColors != null ? mDarkThemeBadgeColors.length : "0(null)");
314     }
315 
316     /** Builder for a {@link UserTypeDetails}; see that class for documentation. */
317     public static final class Builder {
318         // UserTypeDetails properties and their default values.
319         private String mName; // This MUST be explicitly set.
320         private int mBaseType; // This MUST be explicitly set.
321         private int mMaxAllowed = UNLIMITED_NUMBER_OF_USERS;
322         private int mMaxAllowedPerParent = UNLIMITED_NUMBER_OF_USERS;
323         private int mDefaultUserInfoPropertyFlags = 0;
324         private @Nullable Bundle mDefaultRestrictions = null;
325         private boolean mEnabled = true;
326         private int mLabel = Resources.ID_NULL;
327         private @Nullable int[] mBadgeLabels = null;
328         private @Nullable int[] mBadgeColors = null;
329         private @Nullable int[] mDarkThemeBadgeColors = null;
330         private @DrawableRes int mIconBadge = Resources.ID_NULL;
331         private @DrawableRes int mBadgePlain = Resources.ID_NULL;
332         private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL;
333 
setName(String name)334         public Builder setName(String name) {
335             mName = name;
336             return this;
337         }
338 
setEnabled(boolean enabled)339         public Builder setEnabled(boolean enabled) {
340             mEnabled = enabled;
341             return this;
342         }
343 
setMaxAllowed(int maxAllowed)344         public Builder setMaxAllowed(int maxAllowed) {
345             mMaxAllowed = maxAllowed;
346             return this;
347         }
348 
setMaxAllowedPerParent(int maxAllowedPerParent)349         public Builder setMaxAllowedPerParent(int maxAllowedPerParent) {
350             mMaxAllowedPerParent = maxAllowedPerParent;
351             return this;
352         }
353 
setBaseType(@serInfoFlag int baseType)354         public Builder setBaseType(@UserInfoFlag int baseType) {
355             mBaseType = baseType;
356             return this;
357         }
358 
setDefaultUserInfoPropertyFlags(@serInfoFlag int flags)359         public Builder setDefaultUserInfoPropertyFlags(@UserInfoFlag int flags) {
360             mDefaultUserInfoPropertyFlags = flags;
361             return this;
362         }
363 
setBadgeLabels(@tringRes int ... badgeLabels)364         public Builder setBadgeLabels(@StringRes int ... badgeLabels) {
365             mBadgeLabels = badgeLabels;
366             return this;
367         }
368 
setBadgeColors(@olorRes int ... badgeColors)369         public Builder setBadgeColors(@ColorRes int ... badgeColors) {
370             mBadgeColors = badgeColors;
371             return this;
372         }
373 
374         /**
375          * The badge colors when the badge is on a dark background.
376          */
setDarkThemeBadgeColors(@olorRes int ... darkThemeBadgeColors)377         public Builder setDarkThemeBadgeColors(@ColorRes int ... darkThemeBadgeColors) {
378             mDarkThemeBadgeColors = darkThemeBadgeColors;
379             return this;
380         }
381 
setIconBadge(@rawableRes int badgeIcon)382         public Builder setIconBadge(@DrawableRes int badgeIcon) {
383             mIconBadge = badgeIcon;
384             return this;
385         }
386 
setBadgePlain(@rawableRes int badgePlain)387         public Builder setBadgePlain(@DrawableRes int badgePlain) {
388             mBadgePlain = badgePlain;
389             return this;
390         }
391 
setBadgeNoBackground(@rawableRes int badgeNoBackground)392         public Builder setBadgeNoBackground(@DrawableRes int badgeNoBackground) {
393             mBadgeNoBackground = badgeNoBackground;
394             return this;
395         }
396 
setLabel(int label)397         public Builder setLabel(int label) {
398             mLabel = label;
399             return this;
400         }
401 
setDefaultRestrictions(@ullable Bundle restrictions)402         public Builder setDefaultRestrictions(@Nullable Bundle restrictions) {
403             mDefaultRestrictions = restrictions;
404             return this;
405         }
406 
getBaseType()407         @UserInfoFlag int getBaseType() {
408             return mBaseType;
409         }
410 
createUserTypeDetails()411         public UserTypeDetails createUserTypeDetails() {
412             Preconditions.checkArgument(mName != null,
413                     "Cannot create a UserTypeDetails with no name.");
414             Preconditions.checkArgument(hasValidBaseType(),
415                     "UserTypeDetails " + mName + " has invalid baseType: " + mBaseType);
416             Preconditions.checkArgument(hasValidPropertyFlags(),
417                     "UserTypeDetails " + mName + " has invalid flags: "
418                             + Integer.toHexString(mDefaultUserInfoPropertyFlags));
419             if (hasBadge()) {
420                 Preconditions.checkArgument(mBadgeLabels != null && mBadgeLabels.length != 0,
421                         "UserTypeDetails " + mName + " has badge but no badgeLabels.");
422                 Preconditions.checkArgument(mBadgeColors != null && mBadgeColors.length != 0,
423                         "UserTypeDetails " + mName + " has badge but no badgeColors.");
424             }
425             return new UserTypeDetails(mName, mEnabled, mMaxAllowed, mBaseType,
426                     mDefaultUserInfoPropertyFlags, mLabel, mMaxAllowedPerParent,
427                     mIconBadge, mBadgePlain, mBadgeNoBackground, mBadgeLabels, mBadgeColors,
428                     mDarkThemeBadgeColors == null ? mBadgeColors : mDarkThemeBadgeColors,
429                     mDefaultRestrictions);
430         }
431 
hasBadge()432         private boolean hasBadge() {
433             return mIconBadge != Resources.ID_NULL;
434         }
435 
436         // TODO(b/143784345): Refactor this when we clean up UserInfo.
hasValidBaseType()437         private boolean hasValidBaseType() {
438             return mBaseType == UserInfo.FLAG_FULL
439                     || mBaseType == UserInfo.FLAG_PROFILE
440                     || mBaseType == UserInfo.FLAG_SYSTEM
441                     || mBaseType == (UserInfo.FLAG_FULL | UserInfo.FLAG_SYSTEM);
442         }
443 
444         // TODO(b/143784345): Refactor this when we clean up UserInfo.
hasValidPropertyFlags()445         private boolean hasValidPropertyFlags() {
446             final int forbiddenMask =
447                     UserInfo.FLAG_PRIMARY |
448                     UserInfo.FLAG_ADMIN |
449                     UserInfo.FLAG_INITIALIZED |
450                     UserInfo.FLAG_QUIET_MODE |
451                     UserInfo.FLAG_FULL |
452                     UserInfo.FLAG_SYSTEM |
453                     UserInfo.FLAG_PROFILE;
454             return (mDefaultUserInfoPropertyFlags & forbiddenMask) == 0;
455         }
456     }
457 
458     /**
459      * Returns whether the user type is a managed profile
460      * (i.e. {@link UserManager#USER_TYPE_PROFILE_MANAGED}).
461      */
isManagedProfile()462     public boolean isManagedProfile() {
463         return UserManager.isUserTypeManagedProfile(mName);
464     }
465 }
466