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 static android.content.pm.UserInfo.FLAG_DEMO; 20 import static android.content.pm.UserInfo.FLAG_EPHEMERAL; 21 import static android.content.pm.UserInfo.FLAG_FULL; 22 import static android.content.pm.UserInfo.FLAG_GUEST; 23 import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; 24 import static android.content.pm.UserInfo.FLAG_PROFILE; 25 import static android.content.pm.UserInfo.FLAG_RESTRICTED; 26 import static android.content.pm.UserInfo.FLAG_SYSTEM; 27 import static android.os.UserManager.USER_TYPE_FULL_DEMO; 28 import static android.os.UserManager.USER_TYPE_FULL_GUEST; 29 import static android.os.UserManager.USER_TYPE_FULL_RESTRICTED; 30 import static android.os.UserManager.USER_TYPE_FULL_SECONDARY; 31 import static android.os.UserManager.USER_TYPE_FULL_SYSTEM; 32 import static android.os.UserManager.USER_TYPE_PROFILE_CLONE; 33 import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED; 34 import static android.os.UserManager.USER_TYPE_PROFILE_TEST; 35 import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS; 36 37 import static com.android.server.pm.UserTypeDetails.UNLIMITED_NUMBER_OF_USERS; 38 39 import android.content.pm.UserInfo; 40 import android.content.res.Resources; 41 import android.content.res.XmlResourceParser; 42 import android.os.Build; 43 import android.os.Bundle; 44 import android.os.UserManager; 45 import android.util.ArrayMap; 46 import android.util.Slog; 47 48 import com.android.internal.annotations.VisibleForTesting; 49 import com.android.internal.util.XmlUtils; 50 51 import org.xmlpull.v1.XmlPullParserException; 52 53 import java.io.IOException; 54 import java.util.ArrayList; 55 import java.util.List; 56 import java.util.function.Consumer; 57 58 /** 59 * Class for creating all {@link UserTypeDetails} on the device. 60 * 61 * This class is responsible both for defining the AOSP use types, as well as reading in customized 62 * user types from {@link com.android.internal.R.xml#config_user_types}. 63 * 64 * Tests are located in UserManagerServiceUserTypeTest.java. 65 * @hide 66 */ 67 public final class UserTypeFactory { 68 69 private static final String LOG_TAG = "UserTypeFactory"; 70 71 /** This is a utility class, so no instantiable constructor. */ UserTypeFactory()72 private UserTypeFactory() {} 73 74 /** 75 * Obtains the user types (built-in and customized) for this device. 76 * 77 * @return mapping from the name of each user type to its {@link UserTypeDetails} object 78 */ getUserTypes()79 public static ArrayMap<String, UserTypeDetails> getUserTypes() { 80 final ArrayMap<String, UserTypeDetails.Builder> builders = getDefaultBuilders(); 81 82 try (XmlResourceParser parser = 83 Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) { 84 customizeBuilders(builders, parser); 85 } 86 87 final ArrayMap<String, UserTypeDetails> types = new ArrayMap<>(builders.size()); 88 for (int i = 0; i < builders.size(); i++) { 89 types.put(builders.keyAt(i), builders.valueAt(i).createUserTypeDetails()); 90 } 91 return types; 92 } 93 getDefaultBuilders()94 private static ArrayMap<String, UserTypeDetails.Builder> getDefaultBuilders() { 95 final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>(); 96 97 builders.put(USER_TYPE_PROFILE_MANAGED, getDefaultTypeProfileManaged()); 98 builders.put(USER_TYPE_FULL_SYSTEM, getDefaultTypeFullSystem()); 99 builders.put(USER_TYPE_FULL_SECONDARY, getDefaultTypeFullSecondary()); 100 builders.put(USER_TYPE_FULL_GUEST, getDefaultTypeFullGuest()); 101 builders.put(USER_TYPE_FULL_DEMO, getDefaultTypeFullDemo()); 102 builders.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted()); 103 builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless()); 104 builders.put(USER_TYPE_PROFILE_CLONE, getDefaultTypeProfileClone()); 105 if (Build.IS_DEBUGGABLE) { 106 builders.put(USER_TYPE_PROFILE_TEST, getDefaultTypeProfileTest()); 107 } 108 109 return builders; 110 } 111 112 /** 113 * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_CLONE} 114 * configuration. 115 */ 116 // TODO(b/182396009): Add default restrictions, if needed for clone user type. getDefaultTypeProfileClone()117 private static UserTypeDetails.Builder getDefaultTypeProfileClone() { 118 return new UserTypeDetails.Builder() 119 .setName(USER_TYPE_PROFILE_CLONE) 120 .setBaseType(FLAG_PROFILE) 121 .setMaxAllowedPerParent(1) 122 .setLabel(0) 123 .setDefaultRestrictions(null) 124 .setIsMediaSharedWithParent(true); 125 } 126 127 /** 128 * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_MANAGED} 129 * configuration. 130 */ getDefaultTypeProfileManaged()131 private static UserTypeDetails.Builder getDefaultTypeProfileManaged() { 132 return new UserTypeDetails.Builder() 133 .setName(USER_TYPE_PROFILE_MANAGED) 134 .setBaseType(FLAG_PROFILE) 135 .setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE) 136 .setMaxAllowedPerParent(1) 137 .setLabel(0) 138 .setIconBadge(com.android.internal.R.drawable.ic_corp_icon_badge_case) 139 .setBadgePlain(com.android.internal.R.drawable.ic_corp_badge_case) 140 .setBadgeNoBackground(com.android.internal.R.drawable.ic_corp_badge_no_background) 141 .setBadgeLabels( 142 com.android.internal.R.string.managed_profile_label_badge, 143 com.android.internal.R.string.managed_profile_label_badge_2, 144 com.android.internal.R.string.managed_profile_label_badge_3) 145 .setBadgeColors( 146 com.android.internal.R.color.profile_badge_1, 147 com.android.internal.R.color.profile_badge_2, 148 com.android.internal.R.color.profile_badge_3) 149 .setDarkThemeBadgeColors( 150 com.android.internal.R.color.profile_badge_1_dark, 151 com.android.internal.R.color.profile_badge_2_dark, 152 com.android.internal.R.color.profile_badge_3_dark) 153 .setDefaultRestrictions(getDefaultManagedProfileRestrictions()) 154 .setDefaultSecureSettings(getDefaultManagedProfileSecureSettings()) 155 .setDefaultCrossProfileIntentFilters(getDefaultManagedCrossProfileIntentFilter()); 156 } 157 158 /** 159 * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_TEST} 160 * configuration (for userdebug builds). For now it just uses managed profile badges. 161 */ getDefaultTypeProfileTest()162 private static UserTypeDetails.Builder getDefaultTypeProfileTest() { 163 final Bundle restrictions = new Bundle(); 164 restrictions.putBoolean(UserManager.DISALLOW_FUN, true); 165 166 return new UserTypeDetails.Builder() 167 .setName(USER_TYPE_PROFILE_TEST) 168 .setBaseType(FLAG_PROFILE) 169 .setMaxAllowedPerParent(2) 170 .setLabel(0) 171 .setIconBadge(com.android.internal.R.drawable.ic_corp_icon_badge_case) 172 .setBadgePlain(com.android.internal.R.drawable.ic_corp_badge_case) 173 .setBadgeNoBackground(com.android.internal.R.drawable.ic_corp_badge_no_background) 174 .setBadgeLabels( 175 com.android.internal.R.string.managed_profile_label_badge, 176 com.android.internal.R.string.managed_profile_label_badge_2, 177 com.android.internal.R.string.managed_profile_label_badge_3) 178 .setBadgeColors( 179 com.android.internal.R.color.profile_badge_1, 180 com.android.internal.R.color.profile_badge_2, 181 com.android.internal.R.color.profile_badge_3) 182 .setDarkThemeBadgeColors( 183 com.android.internal.R.color.profile_badge_1_dark, 184 com.android.internal.R.color.profile_badge_2_dark, 185 com.android.internal.R.color.profile_badge_3_dark) 186 .setDefaultRestrictions(restrictions); 187 } 188 189 /** 190 * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_SECONDARY} 191 * configuration. 192 */ getDefaultTypeFullSecondary()193 private static UserTypeDetails.Builder getDefaultTypeFullSecondary() { 194 return new UserTypeDetails.Builder() 195 .setName(USER_TYPE_FULL_SECONDARY) 196 .setBaseType(FLAG_FULL) 197 .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS) 198 .setDefaultRestrictions(getDefaultSecondaryUserRestrictions()); 199 } 200 201 /** 202 * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_GUEST} configuration. 203 */ getDefaultTypeFullGuest()204 private static UserTypeDetails.Builder getDefaultTypeFullGuest() { 205 final boolean ephemeralGuests = Resources.getSystem() 206 .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral); 207 final int flags = FLAG_GUEST | (ephemeralGuests ? FLAG_EPHEMERAL : 0); 208 209 return new UserTypeDetails.Builder() 210 .setName(USER_TYPE_FULL_GUEST) 211 .setBaseType(FLAG_FULL) 212 .setDefaultUserInfoPropertyFlags(flags) 213 .setMaxAllowed(1) 214 .setDefaultRestrictions(getDefaultGuestUserRestrictions()); 215 } 216 217 /** 218 * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_DEMO} configuration. 219 */ getDefaultTypeFullDemo()220 private static UserTypeDetails.Builder getDefaultTypeFullDemo() { 221 return new UserTypeDetails.Builder() 222 .setName(USER_TYPE_FULL_DEMO) 223 .setBaseType(FLAG_FULL) 224 .setDefaultUserInfoPropertyFlags(FLAG_DEMO) 225 .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS) 226 .setDefaultRestrictions(null); 227 } 228 229 /** 230 * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_RESTRICTED} 231 * configuration. 232 */ getDefaultTypeFullRestricted()233 private static UserTypeDetails.Builder getDefaultTypeFullRestricted() { 234 return new UserTypeDetails.Builder() 235 .setName(USER_TYPE_FULL_RESTRICTED) 236 .setBaseType(FLAG_FULL) 237 .setDefaultUserInfoPropertyFlags(FLAG_RESTRICTED) 238 .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS) 239 // NB: UserManagerService.createRestrictedProfile() applies hardcoded restrictions. 240 .setDefaultRestrictions(null); 241 } 242 243 /** 244 * Returns the Builder for the default {@link UserManager#USER_TYPE_FULL_SYSTEM} configuration. 245 */ getDefaultTypeFullSystem()246 private static UserTypeDetails.Builder getDefaultTypeFullSystem() { 247 return new UserTypeDetails.Builder() 248 .setName(USER_TYPE_FULL_SYSTEM) 249 .setBaseType(FLAG_SYSTEM | FLAG_FULL); 250 } 251 252 /** 253 * Returns the Builder for the default {@link UserManager#USER_TYPE_SYSTEM_HEADLESS} 254 * configuration. 255 */ getDefaultTypeSystemHeadless()256 private static UserTypeDetails.Builder getDefaultTypeSystemHeadless() { 257 return new UserTypeDetails.Builder() 258 .setName(USER_TYPE_SYSTEM_HEADLESS) 259 .setBaseType(FLAG_SYSTEM); 260 } 261 getDefaultSecondaryUserRestrictions()262 private static Bundle getDefaultSecondaryUserRestrictions() { 263 final Bundle restrictions = new Bundle(); 264 restrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true); 265 restrictions.putBoolean(UserManager.DISALLOW_SMS, true); 266 return restrictions; 267 } 268 getDefaultGuestUserRestrictions()269 private static Bundle getDefaultGuestUserRestrictions() { 270 // Guest inherits the secondary user's restrictions, plus has some extra ones. 271 final Bundle restrictions = getDefaultSecondaryUserRestrictions(); 272 restrictions.putBoolean(UserManager.DISALLOW_CONFIG_WIFI, true); 273 restrictions.putBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true); 274 restrictions.putBoolean(UserManager.DISALLOW_CONFIG_CREDENTIALS, true); 275 return restrictions; 276 } 277 getDefaultManagedProfileRestrictions()278 private static Bundle getDefaultManagedProfileRestrictions() { 279 final Bundle restrictions = new Bundle(); 280 restrictions.putBoolean(UserManager.DISALLOW_WALLPAPER, true); 281 return restrictions; 282 } 283 getDefaultManagedProfileSecureSettings()284 private static Bundle getDefaultManagedProfileSecureSettings() { 285 // Only add String values to the bundle, settings are written as Strings eventually 286 final Bundle settings = new Bundle(); 287 settings.putString( 288 android.provider.Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, "1"); 289 settings.putString( 290 android.provider.Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, "1"); 291 return settings; 292 } 293 294 private static List<DefaultCrossProfileIntentFilter> getDefaultManagedCrossProfileIntentFilter()295 getDefaultManagedCrossProfileIntentFilter() { 296 return DefaultCrossProfileIntentFiltersUtils.getDefaultManagedProfileFilters(); 297 } 298 299 /** 300 * Reads the given xml parser to obtain device user-type customization, and updates the given 301 * map of {@link UserTypeDetails.Builder}s accordingly. 302 * <p> 303 * The xml file can specify the attributes according to the set... methods below. 304 */ 305 // TODO(b/176973369): Add parsing logic to support custom settings/filters 306 // in config_user_types.xml 307 @VisibleForTesting customizeBuilders(ArrayMap<String, UserTypeDetails.Builder> builders, XmlResourceParser parser)308 static void customizeBuilders(ArrayMap<String, UserTypeDetails.Builder> builders, 309 XmlResourceParser parser) { 310 try { 311 XmlUtils.beginDocument(parser, "user-types"); 312 for (XmlUtils.nextElement(parser); 313 parser.getEventType() != XmlResourceParser.END_DOCUMENT; 314 XmlUtils.nextElement(parser)) { 315 final boolean isProfile; 316 final String elementName = parser.getName(); 317 if ("profile-type".equals(elementName)) { 318 isProfile = true; 319 } else if ("full-type".equals(elementName)) { 320 isProfile = false; 321 } else if ("change-user-type".equals(elementName)) { 322 // parsed in parseUserUpgrades 323 XmlUtils.skipCurrentTag(parser); 324 continue; 325 } else { 326 Slog.w(LOG_TAG, "Skipping unknown element " + elementName + " in " 327 + parser.getPositionDescription()); 328 XmlUtils.skipCurrentTag(parser); 329 continue; 330 } 331 332 String typeName = parser.getAttributeValue(null, "name"); 333 if (typeName == null) { 334 Slog.w(LOG_TAG, "Skipping user type with no name in " 335 + parser.getPositionDescription()); 336 XmlUtils.skipCurrentTag(parser); 337 continue; 338 } 339 typeName.intern(); 340 341 UserTypeDetails.Builder builder; 342 if (typeName.startsWith("android.")) { 343 // typeName refers to a AOSP-defined type which we are modifying. 344 Slog.i(LOG_TAG, "Customizing user type " + typeName); 345 builder = builders.get(typeName); 346 if (builder == null) { 347 throw new IllegalArgumentException("Illegal custom user type name " 348 + typeName + ": Non-AOSP user types cannot start with 'android.'"); 349 } 350 final boolean isValid = 351 (isProfile && builder.getBaseType() == UserInfo.FLAG_PROFILE) 352 || (!isProfile && builder.getBaseType() == UserInfo.FLAG_FULL); 353 if (!isValid) { 354 throw new IllegalArgumentException("Wrong base type to customize user type " 355 + "(" + typeName + "), which is type " 356 + UserInfo.flagsToString(builder.getBaseType())); 357 } 358 } else if (isProfile) { 359 // typeName refers to a new OEM-defined profile type which we are defining. 360 Slog.i(LOG_TAG, "Creating custom user type " + typeName); 361 builder = new UserTypeDetails.Builder(); 362 builder.setName(typeName); 363 builder.setBaseType(FLAG_PROFILE); 364 builders.put(typeName, builder); 365 } else { 366 throw new IllegalArgumentException("Creation of non-profile user type " 367 + "(" + typeName + ") is not currently supported."); 368 } 369 370 // Process the attributes (other than name). 371 if (isProfile) { 372 setIntAttribute(parser, "max-allowed-per-parent", 373 builder::setMaxAllowedPerParent); 374 setResAttribute(parser, "icon-badge", builder::setIconBadge); 375 setResAttribute(parser, "badge-plain", builder::setBadgePlain); 376 setResAttribute(parser, "badge-no-background", builder::setBadgeNoBackground); 377 } 378 379 // Process child elements. 380 final int depth = parser.getDepth(); 381 while (XmlUtils.nextElementWithin(parser, depth)) { 382 final String childName = parser.getName(); 383 if ("default-restrictions".equals(childName)) { 384 final Bundle restrictions = UserRestrictionsUtils 385 .readRestrictions(XmlUtils.makeTyped(parser)); 386 builder.setDefaultRestrictions(restrictions); 387 } else if (isProfile && "badge-labels".equals(childName)) { 388 setResAttributeArray(parser, builder::setBadgeLabels); 389 } else if (isProfile && "badge-colors".equals(childName)) { 390 setResAttributeArray(parser, builder::setBadgeColors); 391 } else if (isProfile && "badge-colors-dark".equals(childName)) { 392 setResAttributeArray(parser, builder::setDarkThemeBadgeColors); 393 } else { 394 Slog.w(LOG_TAG, "Unrecognized tag " + childName + " in " 395 + parser.getPositionDescription()); 396 } 397 } 398 } 399 } catch (XmlPullParserException | IOException e) { 400 Slog.w(LOG_TAG, "Cannot read user type configuration file.", e); 401 } 402 } 403 404 /** 405 * If the given attribute exists, gets the int stored in it and performs the given fcn using it. 406 * The stored value must be an int or NumberFormatException will be thrown. 407 * 408 * @param parser xml parser from which to read the attribute 409 * @param attributeName name of the attribute 410 * @param fcn one-int-argument function, 411 * like {@link UserTypeDetails.Builder#setMaxAllowedPerParent(int)} 412 */ setIntAttribute(XmlResourceParser parser, String attributeName, Consumer<Integer> fcn)413 private static void setIntAttribute(XmlResourceParser parser, String attributeName, 414 Consumer<Integer> fcn) { 415 final String intValue = parser.getAttributeValue(null, attributeName); 416 if (intValue == null) { 417 return; 418 } 419 try { 420 fcn.accept(Integer.parseInt(intValue)); 421 } catch (NumberFormatException e) { 422 Slog.e(LOG_TAG, "Cannot parse value of '" + intValue + "' for " + attributeName 423 + " in " + parser.getPositionDescription(), e); 424 throw e; 425 } 426 } 427 428 /** 429 * If the given attribute exists, gets the resId stored in it (or 0 if it is not a valid resId) 430 * and performs the given fcn using it. 431 * 432 * @param parser xml parser from which to read the attribute 433 * @param attributeName name of the attribute 434 * @param fcn one-argument function, like {@link UserTypeDetails.Builder#setIconBadge(int)} 435 */ setResAttribute(XmlResourceParser parser, String attributeName, Consumer<Integer> fcn)436 private static void setResAttribute(XmlResourceParser parser, String attributeName, 437 Consumer<Integer> fcn) { 438 if (parser.getAttributeValue(null, attributeName) == null) { 439 // Attribute is not present, i.e. use the default value. 440 return; 441 } 442 final int resId = parser.getAttributeResourceValue(null, attributeName, Resources.ID_NULL); 443 fcn.accept(resId); 444 } 445 446 /** 447 * Gets the resIds stored in "item" elements (in their "res" attribute) at the current depth. 448 * Then performs the given fcn using the int[] array of these resIds. 449 * <p> 450 * Each xml element is expected to be of the form {@code <item res="someResValue" />}. 451 * 452 * @param parser xml parser from which to read the elements and their attributes 453 * @param fcn function, like {@link UserTypeDetails.Builder#setBadgeColors(int...)} 454 */ setResAttributeArray(XmlResourceParser parser, Consumer<int[]> fcn)455 private static void setResAttributeArray(XmlResourceParser parser, Consumer<int[]> fcn) 456 throws IOException, XmlPullParserException { 457 458 ArrayList<Integer> resList = new ArrayList<>(); 459 final int depth = parser.getDepth(); 460 while (XmlUtils.nextElementWithin(parser, depth)) { 461 final String elementName = parser.getName(); 462 if (!"item".equals(elementName)) { 463 Slog.w(LOG_TAG, "Skipping unknown child element " + elementName + " in " 464 + parser.getPositionDescription()); 465 XmlUtils.skipCurrentTag(parser); 466 continue; 467 } 468 final int resId = parser.getAttributeResourceValue(null, "res", -1); 469 if (resId == -1) { 470 continue; 471 } 472 resList.add(resId); 473 } 474 475 int[] result = new int[resList.size()]; 476 for (int i = 0; i < resList.size(); i++) { 477 result[i] = resList.get(i); 478 } 479 fcn.accept(result); 480 } 481 482 /** 483 * Returns the user type version of the config XML file. 484 * @return user type version defined in XML file, 0 if none. 485 */ getUserTypeVersion()486 public static int getUserTypeVersion() { 487 try (XmlResourceParser parser = 488 Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) { 489 return getUserTypeVersion(parser); 490 } 491 } 492 493 @VisibleForTesting getUserTypeVersion(XmlResourceParser parser)494 static int getUserTypeVersion(XmlResourceParser parser) { 495 int version = 0; 496 497 try { 498 XmlUtils.beginDocument(parser, "user-types"); 499 String versionValue = parser.getAttributeValue(null, "version"); 500 if (versionValue != null) { 501 try { 502 version = Integer.parseInt(versionValue); 503 } catch (NumberFormatException e) { 504 Slog.e(LOG_TAG, "Cannot parse value of '" + versionValue + "' for version in " 505 + parser.getPositionDescription(), e); 506 throw e; 507 } 508 } 509 } catch (XmlPullParserException | IOException e) { 510 Slog.w(LOG_TAG, "Cannot read user type configuration file.", e); 511 } 512 513 return version; 514 } 515 516 /** 517 * Obtains the user type upgrades for this device. 518 * @return The list of user type upgrades. 519 */ getUserTypeUpgrades()520 public static List<UserTypeUpgrade> getUserTypeUpgrades() { 521 final List<UserTypeUpgrade> userUpgrades; 522 try (XmlResourceParser parser = 523 Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) { 524 userUpgrades = parseUserUpgrades(getDefaultBuilders(), parser); 525 } 526 return userUpgrades; 527 } 528 529 @VisibleForTesting parseUserUpgrades( ArrayMap<String, UserTypeDetails.Builder> builders, XmlResourceParser parser)530 static List<UserTypeUpgrade> parseUserUpgrades( 531 ArrayMap<String, UserTypeDetails.Builder> builders, XmlResourceParser parser) { 532 final List<UserTypeUpgrade> userUpgrades = new ArrayList<>(); 533 534 try { 535 XmlUtils.beginDocument(parser, "user-types"); 536 for (XmlUtils.nextElement(parser); 537 parser.getEventType() != XmlResourceParser.END_DOCUMENT; 538 XmlUtils.nextElement(parser)) { 539 final String elementName = parser.getName(); 540 if ("change-user-type".equals(elementName)) { 541 final String fromType = parser.getAttributeValue(null, "from"); 542 final String toType = parser.getAttributeValue(null, "to"); 543 // Check that the base type doesn't change. 544 // Currently, only the base type of PROFILE is supported. 545 validateUserTypeIsProfile(fromType, builders); 546 validateUserTypeIsProfile(toType, builders); 547 548 final int maxVersionToConvert; 549 try { 550 maxVersionToConvert = Integer.parseInt( 551 parser.getAttributeValue(null, "whenVersionLeq")); 552 } catch (NumberFormatException e) { 553 Slog.e(LOG_TAG, "Cannot parse value of whenVersionLeq in " 554 + parser.getPositionDescription(), e); 555 throw e; 556 } 557 558 UserTypeUpgrade userTypeUpgrade = new UserTypeUpgrade(fromType, toType, 559 maxVersionToConvert); 560 userUpgrades.add(userTypeUpgrade); 561 continue; 562 } else { 563 XmlUtils.skipCurrentTag(parser); 564 continue; 565 } 566 } 567 } catch (XmlPullParserException | IOException e) { 568 Slog.w(LOG_TAG, "Cannot read user type configuration file.", e); 569 } 570 571 return userUpgrades; 572 } 573 validateUserTypeIsProfile(String userType, ArrayMap<String, UserTypeDetails.Builder> builders)574 private static void validateUserTypeIsProfile(String userType, 575 ArrayMap<String, UserTypeDetails.Builder> builders) { 576 UserTypeDetails.Builder builder = builders.get(userType); 577 if (builder != null && builder.getBaseType() != FLAG_PROFILE) { 578 throw new IllegalArgumentException("Illegal upgrade of user type " + userType 579 + " : Can only upgrade profiles user types"); 580 } 581 } 582 583 /** 584 * Contains details required for an upgrade operation for {@link UserTypeDetails}; 585 */ 586 public static class UserTypeUpgrade { 587 private final String mFromType; 588 private final String mToType; 589 private final int mUpToVersion; 590 UserTypeUpgrade(String fromType, String toType, int upToVersion)591 public UserTypeUpgrade(String fromType, String toType, int upToVersion) { 592 mFromType = fromType; 593 mToType = toType; 594 mUpToVersion = upToVersion; 595 } 596 getFromType()597 public String getFromType() { 598 return mFromType; 599 } 600 getToType()601 public String getToType() { 602 return mToType; 603 } 604 getUpToVersion()605 public int getUpToVersion() { 606 return mUpToVersion; 607 } 608 } 609 } 610