1 /* 2 * Copyright (C) 2021 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.safetycenter.config; 18 19 import static android.os.Build.VERSION_CODES.TIRAMISU; 20 21 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 22 import static org.xmlpull.v1.XmlPullParser.END_TAG; 23 import static org.xmlpull.v1.XmlPullParser.FEATURE_PROCESS_NAMESPACES; 24 import static org.xmlpull.v1.XmlPullParser.START_DOCUMENT; 25 import static org.xmlpull.v1.XmlPullParser.START_TAG; 26 import static org.xmlpull.v1.XmlPullParser.TEXT; 27 28 import static java.util.Locale.ROOT; 29 import static java.util.Objects.requireNonNull; 30 31 import android.annotation.StringRes; 32 import android.content.res.Resources; 33 import android.safetycenter.config.SafetyCenterConfig; 34 import android.safetycenter.config.SafetySource; 35 import android.safetycenter.config.SafetySourcesGroup; 36 import android.util.Log; 37 38 import androidx.annotation.RequiresApi; 39 40 import com.android.modules.utils.build.SdkLevel; 41 import com.android.permission.flags.Flags; 42 43 import org.xmlpull.v1.XmlPullParser; 44 import org.xmlpull.v1.XmlPullParserException; 45 import org.xmlpull.v1.XmlPullParserFactory; 46 47 import java.io.IOException; 48 import java.io.InputStream; 49 50 /** Parser and validator for {@link SafetyCenterConfig} objects. */ 51 @RequiresApi(TIRAMISU) 52 public final class SafetyCenterConfigParser { 53 54 private static final String TAG = "SafetyCenterConfigParser"; 55 private static final String TAG_SAFETY_CENTER_CONFIG = "safety-center-config"; 56 private static final String TAG_SAFETY_SOURCES_CONFIG = "safety-sources-config"; 57 private static final String TAG_SAFETY_SOURCES_GROUP = "safety-sources-group"; 58 private static final String TAG_STATIC_SAFETY_SOURCE = "static-safety-source"; 59 private static final String TAG_DYNAMIC_SAFETY_SOURCE = "dynamic-safety-source"; 60 private static final String TAG_ISSUE_ONLY_SAFETY_SOURCE = "issue-only-safety-source"; 61 private static final String ATTR_SAFETY_SOURCES_GROUP_ID = "id"; 62 private static final String ATTR_SAFETY_SOURCES_GROUP_TITLE = "title"; 63 private static final String ATTR_SAFETY_SOURCES_GROUP_SUMMARY = "summary"; 64 private static final String ATTR_SAFETY_SOURCES_GROUP_STATELESS_ICON_TYPE = "statelessIconType"; 65 private static final String ATTR_SAFETY_SOURCES_GROUP_TYPE = "type"; 66 private static final String ATTR_SAFETY_SOURCE_ID = "id"; 67 private static final String ATTR_SAFETY_SOURCE_PACKAGE_NAME = "packageName"; 68 private static final String ATTR_SAFETY_SOURCE_TITLE = "title"; 69 private static final String ATTR_SAFETY_SOURCE_TITLE_FOR_WORK = "titleForWork"; 70 private static final String ATTR_SAFETY_SOURCE_TITLE_FOR_PRIVATE_PROFILE = 71 "titleForPrivateProfile"; 72 private static final String ATTR_SAFETY_SOURCE_SUMMARY = "summary"; 73 private static final String ATTR_SAFETY_SOURCE_INTENT_ACTION = "intentAction"; 74 private static final String ATTR_SAFETY_SOURCE_PROFILE = "profile"; 75 private static final String ATTR_SAFETY_SOURCE_INITIAL_DISPLAY_STATE = "initialDisplayState"; 76 private static final String ATTR_SAFETY_SOURCE_MAX_SEVERITY_LEVEL = "maxSeverityLevel"; 77 private static final String ATTR_SAFETY_SOURCE_SEARCH_TERMS = "searchTerms"; 78 private static final String ATTR_SAFETY_SOURCE_LOGGING_ALLOWED = "loggingAllowed"; 79 private static final String ATTR_SAFETY_SOURCE_REFRESH_ON_PAGE_OPEN_ALLOWED = 80 "refreshOnPageOpenAllowed"; 81 private static final String ATTR_SAFETY_SOURCE_NOTIFICATIONS_ALLOWED = "notificationsAllowed"; 82 private static final String ATTR_SAFETY_SOURCE_DEDUPLICATION_GROUP = "deduplicationGroup"; 83 private static final String ATTR_SAFETY_SOURCE_PACKAGE_CERT_HASHES = "packageCertificateHashes"; 84 private static final String ENUM_STATELESS_ICON_TYPE_NONE = "none"; 85 private static final String ENUM_STATELESS_ICON_TYPE_PRIVACY = "privacy"; 86 private static final String ENUM_GROUP_TYPE_STATEFUL = "stateful"; 87 private static final String ENUM_GROUP_TYPE_STATELESS = "stateless"; 88 private static final String ENUM_GROUP_TYPE_HIDDEN = "hidden"; 89 private static final String ENUM_PROFILE_PRIMARY = "primary_profile_only"; 90 private static final String ENUM_PROFILE_ALL = "all_profiles"; 91 private static final String ENUM_INITIAL_DISPLAY_STATE_ENABLED = "enabled"; 92 private static final String ENUM_INITIAL_DISPLAY_STATE_DISABLED = "disabled"; 93 private static final String ENUM_INITIAL_DISPLAY_STATE_HIDDEN = "hidden"; 94 SafetyCenterConfigParser()95 private SafetyCenterConfigParser() {} 96 97 /** 98 * Parses and validates the given XML resource into a {@link SafetyCenterConfig} object. 99 * 100 * <p>It throws a {@link ParseException} if the given XML resource does not comply with the 101 * safety_center_config.xsd schema. 102 * 103 * @param in the raw XML resource representing the Safety Center configuration 104 * @param resources the {@link Resources} retrieved from the package that contains the Safety 105 * Center configuration 106 */ parseXmlResource(InputStream in, Resources resources)107 public static SafetyCenterConfig parseXmlResource(InputStream in, Resources resources) 108 throws ParseException { 109 requireNonNull(in); 110 requireNonNull(resources); 111 try { 112 XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); 113 parser.setFeature(FEATURE_PROCESS_NAMESPACES, true); 114 parser.setInput(in, null); 115 if (parser.getEventType() != START_DOCUMENT) { 116 throw new ParseException("Unexpected parser state"); 117 } 118 parser.nextTag(); 119 validateElementStart(parser, TAG_SAFETY_CENTER_CONFIG); 120 SafetyCenterConfig safetyCenterConfig = parseSafetyCenterConfig(parser, resources); 121 if (parser.getEventType() == TEXT && parser.isWhitespace()) { 122 parser.next(); 123 } 124 if (parser.getEventType() != END_DOCUMENT) { 125 throw new ParseException("Unexpected extra root element"); 126 } 127 return safetyCenterConfig; 128 } catch (XmlPullParserException | IOException e) { 129 throw new ParseException("Exception while parsing the XML resource", e); 130 } 131 } 132 parseSafetyCenterConfig( XmlPullParser parser, Resources resources)133 private static SafetyCenterConfig parseSafetyCenterConfig( 134 XmlPullParser parser, Resources resources) 135 throws XmlPullParserException, IOException, ParseException { 136 validateElementHasNoAttribute(parser, TAG_SAFETY_CENTER_CONFIG); 137 parser.nextTag(); 138 validateElementStart(parser, TAG_SAFETY_SOURCES_CONFIG); 139 validateElementHasNoAttribute(parser, TAG_SAFETY_SOURCES_CONFIG); 140 SafetyCenterConfig.Builder builder = new SafetyCenterConfig.Builder(); 141 parser.nextTag(); 142 while (parser.getEventType() == START_TAG 143 && parser.getName().equals(TAG_SAFETY_SOURCES_GROUP)) { 144 builder.addSafetySourcesGroup(parseSafetySourcesGroup(parser, resources)); 145 } 146 validateElementEnd(parser, TAG_SAFETY_SOURCES_CONFIG); 147 parser.nextTag(); 148 validateElementEnd(parser, TAG_SAFETY_CENTER_CONFIG); 149 parser.next(); 150 try { 151 return builder.build(); 152 } catch (IllegalStateException e) { 153 throw elementInvalid(TAG_SAFETY_SOURCES_CONFIG, e); 154 } 155 } 156 parseSafetySourcesGroup( XmlPullParser parser, Resources resources)157 private static SafetySourcesGroup parseSafetySourcesGroup( 158 XmlPullParser parser, Resources resources) 159 throws XmlPullParserException, IOException, ParseException { 160 String name = TAG_SAFETY_SOURCES_GROUP; 161 SafetySourcesGroup.Builder builder = new SafetySourcesGroup.Builder(); 162 for (int i = 0; i < parser.getAttributeCount(); i++) { 163 switch (parser.getAttributeName(i)) { 164 case ATTR_SAFETY_SOURCES_GROUP_ID: 165 builder.setId( 166 parseStringResourceValue( 167 parser.getAttributeValue(i), 168 name, 169 parser.getAttributeName(i), 170 resources)); 171 break; 172 case ATTR_SAFETY_SOURCES_GROUP_TITLE: 173 builder.setTitleResId( 174 parseStringResourceName( 175 parser.getAttributeValue(i), 176 name, 177 parser.getAttributeName(i), 178 resources)); 179 break; 180 case ATTR_SAFETY_SOURCES_GROUP_SUMMARY: 181 builder.setSummaryResId( 182 parseStringResourceName( 183 parser.getAttributeValue(i), 184 name, 185 parser.getAttributeName(i), 186 resources)); 187 break; 188 case ATTR_SAFETY_SOURCES_GROUP_STATELESS_ICON_TYPE: 189 builder.setStatelessIconType( 190 parseStatelessIconType( 191 parser.getAttributeValue(i), 192 name, 193 parser.getAttributeName(i), 194 resources)); 195 break; 196 case ATTR_SAFETY_SOURCES_GROUP_TYPE: 197 if (SdkLevel.isAtLeastU()) { 198 builder.setType( 199 parseGroupType( 200 parser.getAttributeValue(i), 201 name, 202 parser.getAttributeName(i), 203 resources)); 204 } else { 205 throw attributeUnexpected(name, parser.getAttributeName(i)); 206 } 207 break; 208 default: 209 throw attributeUnexpected(name, parser.getAttributeName(i)); 210 } 211 } 212 parser.nextTag(); 213 loop: 214 while (parser.getEventType() == START_TAG) { 215 int type; 216 switch (parser.getName()) { 217 case TAG_STATIC_SAFETY_SOURCE: 218 type = SafetySource.SAFETY_SOURCE_TYPE_STATIC; 219 break; 220 case TAG_DYNAMIC_SAFETY_SOURCE: 221 type = SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC; 222 break; 223 case TAG_ISSUE_ONLY_SAFETY_SOURCE: 224 type = SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY; 225 break; 226 default: 227 break loop; 228 } 229 builder.addSafetySource(parseSafetySource(parser, resources, type, parser.getName())); 230 } 231 validateElementEnd(parser, name); 232 parser.nextTag(); 233 try { 234 return builder.build(); 235 } catch (IllegalStateException e) { 236 throw elementInvalid(name, e); 237 } 238 } 239 parseSafetySource( XmlPullParser parser, Resources resources, int safetySourceType, String name)240 private static SafetySource parseSafetySource( 241 XmlPullParser parser, Resources resources, int safetySourceType, String name) 242 throws XmlPullParserException, IOException, ParseException { 243 SafetySource.Builder builder = new SafetySource.Builder(safetySourceType); 244 for (int i = 0; i < parser.getAttributeCount(); i++) { 245 switch (parser.getAttributeName(i)) { 246 case ATTR_SAFETY_SOURCE_ID: 247 builder.setId( 248 parseStringResourceValue( 249 parser.getAttributeValue(i), 250 name, 251 parser.getAttributeName(i), 252 resources)); 253 break; 254 case ATTR_SAFETY_SOURCE_PACKAGE_NAME: 255 builder.setPackageName( 256 parseStringResourceValue( 257 parser.getAttributeValue(i), 258 name, 259 parser.getAttributeName(i), 260 resources)); 261 break; 262 case ATTR_SAFETY_SOURCE_TITLE: 263 builder.setTitleResId( 264 parseStringResourceName( 265 parser.getAttributeValue(i), 266 name, 267 parser.getAttributeName(i), 268 resources)); 269 break; 270 case ATTR_SAFETY_SOURCE_TITLE_FOR_WORK: 271 builder.setTitleForWorkResId( 272 parseStringResourceName( 273 parser.getAttributeValue(i), 274 name, 275 parser.getAttributeName(i), 276 resources)); 277 break; 278 case ATTR_SAFETY_SOURCE_TITLE_FOR_PRIVATE_PROFILE: 279 if (SdkLevel.isAtLeastV()) { 280 if (Flags.privateProfileTitleApi()) { 281 builder.setTitleForPrivateProfileResId( 282 parseStringResourceName( 283 parser.getAttributeValue(i), 284 name, 285 parser.getAttributeName(i), 286 resources)); 287 } else { 288 Log.i( 289 TAG, 290 String.format( 291 "Ignoring attribute %s.%s", 292 name, ATTR_SAFETY_SOURCE_TITLE_FOR_PRIVATE_PROFILE)); 293 } 294 break; 295 } else { 296 throw attributeUnexpected(name, parser.getAttributeName(i)); 297 } 298 case ATTR_SAFETY_SOURCE_SUMMARY: 299 builder.setSummaryResId( 300 parseStringResourceName( 301 parser.getAttributeValue(i), 302 name, 303 parser.getAttributeName(i), 304 resources)); 305 break; 306 case ATTR_SAFETY_SOURCE_INTENT_ACTION: 307 builder.setIntentAction( 308 parseStringResourceValue( 309 parser.getAttributeValue(i), 310 name, 311 parser.getAttributeName(i), 312 resources)); 313 break; 314 case ATTR_SAFETY_SOURCE_PROFILE: 315 builder.setProfile( 316 parseProfile( 317 parser.getAttributeValue(i), 318 name, 319 parser.getAttributeName(i), 320 resources)); 321 break; 322 case ATTR_SAFETY_SOURCE_INITIAL_DISPLAY_STATE: 323 builder.setInitialDisplayState( 324 parseInitialDisplayState( 325 parser.getAttributeValue(i), 326 name, 327 parser.getAttributeName(i), 328 resources)); 329 break; 330 case ATTR_SAFETY_SOURCE_MAX_SEVERITY_LEVEL: 331 builder.setMaxSeverityLevel( 332 parseInteger( 333 parser.getAttributeValue(i), 334 name, 335 parser.getAttributeName(i), 336 resources)); 337 break; 338 case ATTR_SAFETY_SOURCE_SEARCH_TERMS: 339 builder.setSearchTermsResId( 340 parseStringResourceName( 341 parser.getAttributeValue(i), 342 name, 343 parser.getAttributeName(i), 344 resources)); 345 break; 346 case ATTR_SAFETY_SOURCE_LOGGING_ALLOWED: 347 builder.setLoggingAllowed( 348 parseBoolean( 349 parser.getAttributeValue(i), 350 name, 351 parser.getAttributeName(i), 352 resources)); 353 break; 354 case ATTR_SAFETY_SOURCE_REFRESH_ON_PAGE_OPEN_ALLOWED: 355 builder.setRefreshOnPageOpenAllowed( 356 parseBoolean( 357 parser.getAttributeValue(i), 358 name, 359 parser.getAttributeName(i), 360 resources)); 361 break; 362 case ATTR_SAFETY_SOURCE_NOTIFICATIONS_ALLOWED: 363 if (SdkLevel.isAtLeastU()) { 364 builder.setNotificationsAllowed( 365 parseBoolean( 366 parser.getAttributeValue(i), 367 name, 368 parser.getAttributeName(i), 369 resources)); 370 } else { 371 throw attributeUnexpected(name, parser.getAttributeName(i)); 372 } 373 break; 374 case ATTR_SAFETY_SOURCE_DEDUPLICATION_GROUP: 375 if (SdkLevel.isAtLeastU()) { 376 builder.setDeduplicationGroup( 377 parseStringResourceValue( 378 parser.getAttributeValue(i), 379 name, 380 parser.getAttributeName(i), 381 resources)); 382 } else { 383 throw attributeUnexpected(name, parser.getAttributeName(i)); 384 } 385 break; 386 case ATTR_SAFETY_SOURCE_PACKAGE_CERT_HASHES: 387 if (SdkLevel.isAtLeastU()) { 388 String commaSeparatedHashes = 389 parseStringResourceValue( 390 parser.getAttributeValue(i), 391 name, 392 parser.getAttributeName(i), 393 resources); 394 String[] splits = commaSeparatedHashes.split(","); 395 for (int j = 0; j < splits.length; j++) { 396 builder.addPackageCertificateHash(splits[j]); 397 } 398 } else { 399 throw attributeUnexpected(name, parser.getAttributeName(i)); 400 } 401 break; 402 default: 403 throw attributeUnexpected(name, parser.getAttributeName(i)); 404 } 405 } 406 parser.nextTag(); 407 validateElementEnd(parser, name); 408 parser.nextTag(); 409 try { 410 return builder.build(); 411 } catch (IllegalStateException e) { 412 throw elementInvalid(name, e); 413 } 414 } 415 validateElementStart(XmlPullParser parser, String name)416 private static void validateElementStart(XmlPullParser parser, String name) 417 throws XmlPullParserException, ParseException { 418 if (parser.getEventType() != START_TAG || !parser.getName().equals(name)) { 419 throw elementMissing(name); 420 } 421 } 422 validateElementEnd(XmlPullParser parser, String name)423 private static void validateElementEnd(XmlPullParser parser, String name) 424 throws XmlPullParserException, ParseException { 425 if (parser.getEventType() != END_TAG || !parser.getName().equals(name)) { 426 throw elementNotClosed(name); 427 } 428 } 429 validateElementHasNoAttribute(XmlPullParser parser, String name)430 private static void validateElementHasNoAttribute(XmlPullParser parser, String name) 431 throws ParseException { 432 if (parser.getAttributeCount() != 0) { 433 throw elementInvalid(name); 434 } 435 } 436 elementMissing(String name)437 private static ParseException elementMissing(String name) { 438 return new ParseException(String.format("Element %s missing", name)); 439 } 440 elementNotClosed(String name)441 private static ParseException elementNotClosed(String name) { 442 return new ParseException(String.format("Element %s not closed", name)); 443 } 444 elementInvalid(String name)445 private static ParseException elementInvalid(String name) { 446 return new ParseException(String.format("Element %s invalid", name)); 447 } 448 elementInvalid(String name, Throwable e)449 private static ParseException elementInvalid(String name, Throwable e) { 450 return new ParseException(String.format("Element %s invalid", name), e); 451 } 452 attributeUnexpected(String parent, String name)453 private static ParseException attributeUnexpected(String parent, String name) { 454 return new ParseException(String.format("Unexpected attribute %s.%s", parent, name)); 455 } 456 attributeInvalidString(String valueString, String parent, String name)457 private static String attributeInvalidString(String valueString, String parent, String name) { 458 return String.format("Attribute value \"%s\" in %s.%s invalid", valueString, parent, name); 459 } 460 attributeInvalid(String valueString, String parent, String name)461 private static ParseException attributeInvalid(String valueString, String parent, String name) { 462 return new ParseException(attributeInvalidString(valueString, parent, name)); 463 } 464 attributeInvalid( String valueString, String parent, String name, Throwable ex)465 private static ParseException attributeInvalid( 466 String valueString, String parent, String name, Throwable ex) { 467 return new ParseException(attributeInvalidString(valueString, parent, name), ex); 468 } 469 parseInteger( String valueString, String parent, String name, Resources resources)470 private static int parseInteger( 471 String valueString, String parent, String name, Resources resources) 472 throws ParseException { 473 String valueToParse = getValueToParse(valueString, parent, name, resources); 474 try { 475 return Integer.parseInt(valueToParse); 476 } catch (NumberFormatException e) { 477 throw attributeInvalid(valueToParse, parent, name, e); 478 } 479 } 480 parseBoolean( String valueString, String parent, String name, Resources resources)481 private static boolean parseBoolean( 482 String valueString, String parent, String name, Resources resources) 483 throws ParseException { 484 String valueToParse = 485 getValueToParse(valueString, parent, name, resources).toLowerCase(ROOT); 486 if (valueToParse.equals("true")) { 487 return true; 488 } else if (!valueToParse.equals("false")) { 489 throw attributeInvalid(valueToParse, parent, name); 490 } 491 return false; 492 } 493 494 @StringRes parseStringResourceName( String valueString, String parent, String name, Resources resources)495 private static int parseStringResourceName( 496 String valueString, String parent, String name, Resources resources) 497 throws ParseException { 498 if (valueString.isEmpty()) { 499 throw new ParseException( 500 String.format("Resource name in %s.%s cannot be empty", parent, name)); 501 } 502 if (valueString.charAt(0) != '@') { 503 throw new ParseException( 504 String.format( 505 "Resource name \"%s\" in %s.%s does not start with @", 506 valueString, parent, name)); 507 } 508 String[] colonSplit = valueString.substring(1).split(":", 2); 509 if (colonSplit.length != 2 || colonSplit[0].isEmpty()) { 510 throw new ParseException( 511 String.format( 512 "Resource name \"%s\" in %s.%s does not specify a package", 513 valueString, parent, name)); 514 } 515 String packageName = colonSplit[0]; 516 String[] slashSplit = colonSplit[1].split("/", 2); 517 if (slashSplit.length != 2 || slashSplit[0].isEmpty()) { 518 throw new ParseException( 519 String.format( 520 "Resource name \"%s\" in %s.%s does not specify a type", 521 valueString, parent, name)); 522 } 523 String type = slashSplit[0]; 524 if (!type.equals("string")) { 525 throw new ParseException( 526 String.format( 527 "Resource name \"%s\" in %s.%s is not a string", 528 valueString, parent, name)); 529 } 530 String entry = slashSplit[1]; 531 int id = resources.getIdentifier(entry, type, packageName); 532 if (id == Resources.ID_NULL) { 533 throw new ParseException( 534 String.format( 535 "Resource name \"%s\" in %s.%s missing or invalid", 536 valueString, parent, name)); 537 } 538 return id; 539 } 540 parseStringResourceValue( String valueString, String parent, String name, Resources resources)541 private static String parseStringResourceValue( 542 String valueString, String parent, String name, Resources resources) { 543 return getValueToParse(valueString, parent, name, resources); 544 } 545 parseStatelessIconType( String valueString, String parent, String name, Resources resources)546 private static int parseStatelessIconType( 547 String valueString, String parent, String name, Resources resources) 548 throws ParseException { 549 String valueToParse = getValueToParse(valueString, parent, name, resources); 550 switch (valueToParse) { 551 case ENUM_STATELESS_ICON_TYPE_NONE: 552 return SafetySourcesGroup.STATELESS_ICON_TYPE_NONE; 553 case ENUM_STATELESS_ICON_TYPE_PRIVACY: 554 return SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY; 555 default: 556 throw attributeInvalid(valueToParse, parent, name); 557 } 558 } 559 parseGroupType( String valueString, String parent, String name, Resources resources)560 private static int parseGroupType( 561 String valueString, String parent, String name, Resources resources) 562 throws ParseException { 563 String valueToParse = getValueToParse(valueString, parent, name, resources); 564 switch (valueToParse) { 565 case ENUM_GROUP_TYPE_STATEFUL: 566 return SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL; 567 case ENUM_GROUP_TYPE_STATELESS: 568 return SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS; 569 case ENUM_GROUP_TYPE_HIDDEN: 570 return SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN; 571 default: 572 throw attributeInvalid(valueToParse, parent, name); 573 } 574 } 575 parseProfile( String valueString, String parent, String name, Resources resources)576 private static int parseProfile( 577 String valueString, String parent, String name, Resources resources) 578 throws ParseException { 579 String valueToParse = getValueToParse(valueString, parent, name, resources); 580 switch (valueToParse) { 581 case ENUM_PROFILE_PRIMARY: 582 return SafetySource.PROFILE_PRIMARY; 583 case ENUM_PROFILE_ALL: 584 return SafetySource.PROFILE_ALL; 585 default: 586 throw attributeInvalid(valueToParse, parent, name); 587 } 588 } 589 parseInitialDisplayState( String valueString, String parent, String name, Resources resources)590 private static int parseInitialDisplayState( 591 String valueString, String parent, String name, Resources resources) 592 throws ParseException { 593 String valueToParse = getValueToParse(valueString, parent, name, resources); 594 switch (valueToParse) { 595 case ENUM_INITIAL_DISPLAY_STATE_ENABLED: 596 return SafetySource.INITIAL_DISPLAY_STATE_ENABLED; 597 case ENUM_INITIAL_DISPLAY_STATE_DISABLED: 598 return SafetySource.INITIAL_DISPLAY_STATE_DISABLED; 599 case ENUM_INITIAL_DISPLAY_STATE_HIDDEN: 600 return SafetySource.INITIAL_DISPLAY_STATE_HIDDEN; 601 default: 602 throw attributeInvalid(valueToParse, parent, name); 603 } 604 } 605 getValueToParse( String valueString, String parent, String name, Resources resources)606 private static String getValueToParse( 607 String valueString, String parent, String name, Resources resources) { 608 try { 609 int id = parseStringResourceName(valueString, parent, name, resources); 610 return resources.getString(id); 611 } catch (ParseException e) { 612 return valueString; 613 } 614 } 615 } 616