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