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