summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--SafetyCenter/Config/TEST_MAPPING7
-rw-r--r--SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java255
-rw-r--r--SafetyCenter/Config/tests/java/com/android/safetycenter/config/Coroutines.kt17
-rw-r--r--SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt220
-rw-r--r--SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigOverlayTest.kt78
-rw-r--r--SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt18
-rw-r--r--SafetyCenter/Config/tests/res/raw/config_dynamic_safety_source_hidden_with_intent.xml15
-rw-r--r--SafetyCenter/Config/tests/res/raw/config_dynamic_safety_source_hidden_with_summary.xml15
-rw-r--r--SafetyCenter/Config/tests/res/raw/config_dynamic_safety_source_hidden_with_title.xml15
-rw-r--r--SafetyCenter/Config/tests/res/raw/config_valid.xml15
-rw-r--r--SafetyCenter/Config/tests/res/values/strings.xml9
-rw-r--r--SafetyCenter/ConfigLintChecker/jarjar-rules.txt1
-rw-r--r--SafetyCenter/ConfigLintChecker/java/android/content/res/Resources.java37
-rw-r--r--SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt90
-rw-r--r--SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt47
-rw-r--r--framework-s/java/android/safetycenter/config/SafetySource.java11
-rw-r--r--framework-s/java/android/safetycenter/config/TEST_MAPPING7
-rw-r--r--framework-s/java/android/safetycenter/config/safety_center_config.xsd113
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt6
19 files changed, 590 insertions, 386 deletions
diff --git a/SafetyCenter/Config/TEST_MAPPING b/SafetyCenter/Config/TEST_MAPPING
new file mode 100644
index 000000000..a39176e27
--- /dev/null
+++ b/SafetyCenter/Config/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "SafetyCenterConfigTests"
+ }
+ ]
+}
diff --git a/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java b/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java
index fc933bdcd..8081ae472 100644
--- a/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java
+++ b/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java
@@ -79,8 +79,7 @@ public final class SafetyCenterConfigParser {
private static final String ENUM_INITIAL_DISPLAY_STATE_DISABLED = "disabled";
private static final String ENUM_INITIAL_DISPLAY_STATE_HIDDEN = "hidden";
- private SafetyCenterConfigParser() {
- }
+ private SafetyCenterConfigParser() {}
/**
* Parses and validates the given XML resource into a {@link SafetyCenterConfig} object.
@@ -88,9 +87,9 @@ public final class SafetyCenterConfigParser {
* <p>It throws a {@link ParseException} if the given XML resource does not comply with the
* safety_center_config.xsd schema.
*
- * @param in the raw XML resource representing the Safety Center configuration
+ * @param in the raw XML resource representing the Safety Center configuration
* @param resources the {@link Resources} retrieved from the package that contains the Safety
- * Center configuration
+ * Center configuration
*/
@NonNull
public static SafetyCenterConfig parseXmlResource(
@@ -106,8 +105,7 @@ public final class SafetyCenterConfigParser {
}
parser.nextTag();
validateElementStart(parser, TAG_SAFETY_CENTER_CONFIG);
- SafetyCenterConfig safetyCenterConfig =
- parseSafetyCenterConfig(parser, resources);
+ SafetyCenterConfig safetyCenterConfig = parseSafetyCenterConfig(parser, resources);
if (parser.getEventType() == TEXT && parser.isWhitespace()) {
parser.next();
}
@@ -132,8 +130,7 @@ public final class SafetyCenterConfigParser {
parser.nextTag();
while (parser.getEventType() == START_TAG
&& parser.getName().equals(TAG_SAFETY_SOURCES_GROUP)) {
- builder.addSafetySourcesGroup(
- parseSafetySourcesGroup(parser, resources));
+ builder.addSafetySourcesGroup(parseSafetySourcesGroup(parser, resources));
}
validateElementEnd(parser, TAG_SAFETY_SOURCES_CONFIG);
parser.nextTag();
@@ -155,22 +152,36 @@ public final class SafetyCenterConfigParser {
for (int i = 0; i < parser.getAttributeCount(); i++) {
switch (parser.getAttributeName(i)) {
case ATTR_SAFETY_SOURCES_GROUP_ID:
- builder.setId(parser.getAttributeValue(i));
+ builder.setId(
+ parseStringResourceValue(
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
break;
case ATTR_SAFETY_SOURCES_GROUP_TITLE:
builder.setTitleResId(
- parseStringResourceName(parser.getAttributeValue(i), name,
- parser.getAttributeName(i), resources));
+ parseStringResourceName(
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
break;
case ATTR_SAFETY_SOURCES_GROUP_SUMMARY:
builder.setSummaryResId(
- parseStringResourceName(parser.getAttributeValue(i), name,
- parser.getAttributeName(i), resources));
+ parseStringResourceName(
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
break;
case ATTR_SAFETY_SOURCES_GROUP_STATELESS_ICON_TYPE:
builder.setStatelessIconType(
parseStatelessIconType(
- parser.getAttributeValue(i), name, parser.getAttributeName(i)));
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
break;
default:
throw attributeUnexpected(name, parser.getAttributeName(i));
@@ -193,8 +204,7 @@ public final class SafetyCenterConfigParser {
default:
break loop;
}
- builder.addSafetySource(
- parseSafetySource(parser, resources, type, parser.getName()));
+ builder.addSafetySource(parseSafetySource(parser, resources, type, parser.getName()));
}
validateElementEnd(parser, name);
parser.nextTag();
@@ -210,64 +220,106 @@ public final class SafetyCenterConfigParser {
@NonNull XmlPullParser parser,
@NonNull Resources resources,
int safetySourceType,
- @NonNull String name
- ) throws XmlPullParserException, IOException, ParseException {
+ @NonNull String name)
+ throws XmlPullParserException, IOException, ParseException {
SafetySource.Builder builder = new SafetySource.Builder(safetySourceType);
for (int i = 0; i < parser.getAttributeCount(); i++) {
switch (parser.getAttributeName(i)) {
case ATTR_SAFETY_SOURCE_ID:
- builder.setId(parser.getAttributeValue(i));
+ builder.setId(
+ parseStringResourceValue(
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
break;
case ATTR_SAFETY_SOURCE_PACKAGE_NAME:
- builder.setPackageName(parser.getAttributeValue(i));
+ builder.setPackageName(
+ parseStringResourceValue(
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
break;
case ATTR_SAFETY_SOURCE_TITLE:
builder.setTitleResId(
- parseStringResourceName(parser.getAttributeValue(i), name,
- parser.getAttributeName(i), resources));
+ parseStringResourceName(
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
break;
case ATTR_SAFETY_SOURCE_TITLE_FOR_WORK:
builder.setTitleForWorkResId(
- parseStringResourceName(parser.getAttributeValue(i), name,
- parser.getAttributeName(i), resources));
+ parseStringResourceName(
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
break;
case ATTR_SAFETY_SOURCE_SUMMARY:
builder.setSummaryResId(
- parseStringResourceName(parser.getAttributeValue(i), name,
- parser.getAttributeName(i), resources));
+ parseStringResourceName(
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
break;
case ATTR_SAFETY_SOURCE_INTENT_ACTION:
- builder.setIntentAction(parser.getAttributeValue(i));
+ builder.setIntentAction(
+ parseStringResourceValue(
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
break;
case ATTR_SAFETY_SOURCE_PROFILE:
builder.setProfile(
- parseProfile(parser.getAttributeValue(i), name,
- parser.getAttributeName(i)));
+ parseProfile(
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
break;
case ATTR_SAFETY_SOURCE_INITIAL_DISPLAY_STATE:
builder.setInitialDisplayState(
parseInitialDisplayState(
- parser.getAttributeValue(i), name, parser.getAttributeName(i)));
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
break;
case ATTR_SAFETY_SOURCE_MAX_SEVERITY_LEVEL:
builder.setMaxSeverityLevel(
- parseInteger(parser.getAttributeValue(i), name,
- parser.getAttributeName(i)));
+ parseInteger(
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
break;
case ATTR_SAFETY_SOURCE_SEARCH_TERMS:
builder.setSearchTermsResId(
- parseStringResourceName(parser.getAttributeValue(i), name,
- parser.getAttributeName(i), resources));
+ parseStringResourceName(
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
break;
case ATTR_SAFETY_SOURCE_LOGGING_ALLOWED:
builder.setLoggingAllowed(
- parseBoolean(parser.getAttributeValue(i), name,
- parser.getAttributeName(i)));
+ parseBoolean(
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
break;
case ATTR_SAFETY_SOURCE_REFRESH_ON_PAGE_OPEN_ALLOWED:
builder.setRefreshOnPageOpenAllowed(
- parseBoolean(parser.getAttributeValue(i), name,
- parser.getAttributeName(i)));
+ parseBoolean(
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
break;
default:
throw attributeUnexpected(name, parser.getAttributeName(i));
@@ -320,109 +372,160 @@ public final class SafetyCenterConfigParser {
return new ParseException(String.format("Element %s invalid", name), e);
}
- private static ParseException attributeUnexpected(@NonNull String parent,
- @NonNull String name) {
+ private static ParseException attributeUnexpected(
+ @NonNull String parent, @NonNull String name) {
return new ParseException(String.format("Unexpected attribute %s.%s", parent, name));
}
- private static ParseException attributeInvalid(@NonNull String parent, @NonNull String name) {
- return new ParseException(String.format("Attribute %s.%s invalid", parent, name));
+ private static String attributeInvalidString(
+ @NonNull String valueString, @NonNull String parent, @NonNull String name) {
+ return String.format("Attribute value \"%s\" in %s.%s invalid", valueString, parent, name);
+ }
+
+ private static ParseException attributeInvalid(
+ @NonNull String valueString, @NonNull String parent, @NonNull String name) {
+ return new ParseException(attributeInvalidString(valueString, parent, name));
+ }
+
+ private static ParseException attributeInvalid(
+ @NonNull String valueString,
+ @NonNull String parent,
+ @NonNull String name,
+ @NonNull Throwable ex) {
+ return new ParseException(attributeInvalidString(valueString, parent, name), ex);
}
private static int parseInteger(
- @NonNull String valueString, @NonNull String parent, @NonNull String name)
+ @NonNull String valueString,
+ @NonNull String parent,
+ @NonNull String name,
+ @NonNull Resources resources)
throws ParseException {
+ String valueToParse = getValueToParse(valueString, parent, name, resources);
try {
- return Integer.parseInt(valueString);
+ return Integer.parseInt(valueToParse);
} catch (NumberFormatException e) {
- throw new ParseException(String.format("Attribute %s.%s invalid", parent, name), e);
+ throw attributeInvalid(valueToParse, parent, name, e);
}
}
private static boolean parseBoolean(
- @NonNull String valueString, @NonNull String parent, @NonNull String name)
+ @NonNull String valueString,
+ @NonNull String parent,
+ @NonNull String name,
+ @NonNull Resources resources)
throws ParseException {
- String valueLowerString = valueString.toLowerCase(ROOT);
- if (valueLowerString.equals("true")) {
+ String valueToParse =
+ getValueToParse(valueString, parent, name, resources).toLowerCase(ROOT);
+ if (valueToParse.equals("true")) {
return true;
- } else if (!valueLowerString.equals("false")) {
- throw new ParseException(String.format("Attribute %s.%s invalid", parent, name));
+ } else if (!valueToParse.equals("false")) {
+ throw attributeInvalid(valueToParse, parent, name);
}
return false;
}
@StringRes
private static int parseStringResourceName(
- @NonNull String valueString, @NonNull String parent, @NonNull String name,
- @NonNull Resources resources) throws ParseException {
+ @NonNull String valueString,
+ @NonNull String parent,
+ @NonNull String name,
+ @NonNull Resources resources)
+ throws ParseException {
if (valueString.isEmpty()) {
throw new ParseException(
String.format("Resource name in %s.%s cannot be empty", parent, name));
}
if (valueString.charAt(0) != '@') {
throw new ParseException(
- String.format("Resource name %s in %s.%s does not start with @", valueString,
- parent, name));
+ String.format(
+ "Resource name \"%s\" in %s.%s does not start with @",
+ valueString, parent, name));
}
String[] colonSplit = valueString.substring(1).split(":", 2);
if (colonSplit.length != 2 || colonSplit[0].isEmpty()) {
throw new ParseException(
- String.format("Resource name %s in %s.%s does not specify a package",
+ String.format(
+ "Resource name \"%s\" in %s.%s does not specify a package",
valueString, parent, name));
}
String packageName = colonSplit[0];
String[] slashSplit = colonSplit[1].split("/", 2);
if (slashSplit.length != 2 || slashSplit[0].isEmpty()) {
throw new ParseException(
- String.format("Resource name %s in %s.%s does not specify a type",
+ String.format(
+ "Resource name \"%s\" in %s.%s does not specify a type",
valueString, parent, name));
}
String type = slashSplit[0];
if (!type.equals("string")) {
throw new ParseException(
- String.format("Resource name %s in %s.%s is not a string", valueString, parent,
- name));
+ String.format(
+ "Resource name \"%s\" in %s.%s is not a string",
+ valueString, parent, name));
}
String entry = slashSplit[1];
int id = resources.getIdentifier(entry, type, packageName);
if (id == Resources.ID_NULL) {
throw new ParseException(
- String.format("Resource name %s in %s.%s missing or invalid", valueString,
- parent, name));
+ String.format(
+ "Resource name \"%s\" in %s.%s missing or invalid",
+ valueString, parent, name));
}
return id;
}
+ @NonNull
+ private static String parseStringResourceValue(
+ @NonNull String valueString,
+ @NonNull String parent,
+ @NonNull String name,
+ @NonNull Resources resources) {
+ return getValueToParse(valueString, parent, name, resources);
+ }
+
private static int parseStatelessIconType(
- @NonNull String valueString, @NonNull String parent, @NonNull String name)
+ @NonNull String valueString,
+ @NonNull String parent,
+ @NonNull String name,
+ @NonNull Resources resources)
throws ParseException {
- switch (valueString) {
+ String valueToParse = getValueToParse(valueString, parent, name, resources);
+ switch (valueToParse) {
case ENUM_STATELESS_ICON_TYPE_NONE:
return SafetySourcesGroup.STATELESS_ICON_TYPE_NONE;
case ENUM_STATELESS_ICON_TYPE_PRIVACY:
return SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY;
default:
- throw attributeInvalid(parent, name);
+ throw attributeInvalid(valueToParse, parent, name);
}
}
private static int parseProfile(
- @NonNull String valueString, @NonNull String parent, @NonNull String name)
+ @NonNull String valueString,
+ @NonNull String parent,
+ @NonNull String name,
+ @NonNull Resources resources)
throws ParseException {
- switch (valueString) {
+ String valueToParse = getValueToParse(valueString, parent, name, resources);
+ switch (valueToParse) {
case ENUM_PROFILE_PRIMARY:
return SafetySource.PROFILE_PRIMARY;
case ENUM_PROFILE_ALL:
return SafetySource.PROFILE_ALL;
default:
- throw attributeInvalid(parent, name);
+ throw attributeInvalid(valueToParse, parent, name);
}
}
private static int parseInitialDisplayState(
- @NonNull String valueString, @NonNull String parent, @NonNull String name)
+ @NonNull String valueString,
+ @NonNull String parent,
+ @NonNull String name,
+ @NonNull Resources resources)
throws ParseException {
- switch (valueString) {
+ String valueToParse = getValueToParse(valueString, parent, name, resources);
+ switch (valueToParse) {
case ENUM_INITIAL_DISPLAY_STATE_ENABLED:
return SafetySource.INITIAL_DISPLAY_STATE_ENABLED;
case ENUM_INITIAL_DISPLAY_STATE_DISABLED:
@@ -430,7 +533,21 @@ public final class SafetyCenterConfigParser {
case ENUM_INITIAL_DISPLAY_STATE_HIDDEN:
return SafetySource.INITIAL_DISPLAY_STATE_HIDDEN;
default:
- throw attributeInvalid(parent, name);
+ throw attributeInvalid(valueToParse, parent, name);
+ }
+ }
+
+ @NonNull
+ private static String getValueToParse(
+ @NonNull String valueString,
+ @NonNull String parent,
+ @NonNull String name,
+ @NonNull Resources resources) {
+ try {
+ int id = parseStringResourceName(valueString, parent, name, resources);
+ return resources.getString(id);
+ } catch (ParseException e) {
+ return valueString;
}
}
}
diff --git a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/Coroutines.kt b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/Coroutines.kt
index 6237348dd..9b1d4c5f9 100644
--- a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/Coroutines.kt
+++ b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/Coroutines.kt
@@ -16,6 +16,7 @@
package com.android.safetycenter.config
+import android.util.Log
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
@@ -52,6 +53,22 @@ object Coroutines {
return waitFor(checkPeriod, condition)
}
+ /** Retries a test until no assertions or exceptions are thrown or a timeout occurs. */
+ fun waitForTestToPass(test: () -> Unit) {
+ waitForWithTimeout {
+ try {
+ test()
+ true
+ } catch (ex: Throwable) {
+ Log.w(TAG, "Encountered test failure, retrying until timeout: $ex")
+ false
+ }
+ }
+ }
+
+ /** A medium period, to be used for conditions that are expected to change. */
+ private val TAG = "Coroutines"
+
/** A medium period, to be used for conditions that are expected to change. */
private val CHECK_PERIOD = Duration.ofMillis(250)
diff --git a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt
index 56a68125d..a06eaa9fb 100644
--- a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt
+++ b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt
@@ -41,16 +41,16 @@ class ParserConfigInvalidTest {
override fun toString() = testName
}
- @Parameterized.Parameter
- lateinit var params: Params
+ @Parameterized.Parameter lateinit var params: Params
@Test
fun invalidConfig_throws() {
val inputStream = context.resources.openRawResource(params.configResourceId)
- val thrown = assertThrows(ParseException::class.java) {
- SafetyCenterConfigParser.parseXmlResource(inputStream, context.resources)
- }
+ val thrown =
+ assertThrows(ParseException::class.java) {
+ SafetyCenterConfigParser.parseXmlResource(inputStream, context.resources)
+ }
assertThat(thrown).hasMessageThat().isEqualTo(params.errorMessage)
if (params.causeErrorMessage != null) {
@@ -67,362 +67,288 @@ class ParserConfigInvalidTest {
"ConfigDynamicSafetySourceAllDisabledNoWork",
R.raw.config_dynamic_safety_source_all_disabled_no_work,
"Element dynamic-safety-source invalid",
- "Required attribute titleForWork missing"
- ),
+ "Required attribute titleForWork missing"),
Params(
"ConfigDynamicSafetySourceAllHiddenWithSearchNoWork",
R.raw.config_dynamic_safety_source_all_hidden_with_search_no_work,
"Element dynamic-safety-source invalid",
- "Required attribute titleForWork missing"
- ),
+ "Required attribute titleForWork missing"),
Params(
"ConfigDynamicSafetySourceAllNoWork",
R.raw.config_dynamic_safety_source_all_no_work,
"Element dynamic-safety-source invalid",
- "Required attribute titleForWork missing"
- ),
+ "Required attribute titleForWork missing"),
Params(
"ConfigDynamicSafetySourceDisabledNoSummary",
R.raw.config_dynamic_safety_source_disabled_no_summary,
"Element dynamic-safety-source invalid",
- "Required attribute summary missing"
- ),
+ "Required attribute summary missing"),
Params(
"ConfigDynamicSafetySourceDisabledNoTitle",
R.raw.config_dynamic_safety_source_disabled_no_title,
"Element dynamic-safety-source invalid",
- "Required attribute title missing"
- ),
+ "Required attribute title missing"),
Params(
"ConfigDynamicSafetySourceDuplicateKey",
R.raw.config_dynamic_safety_source_duplicate_key,
"Element safety-sources-config invalid",
- "Duplicate id id among safety sources"
- ),
- Params(
- "ConfigDynamicSafetySourceHiddenWithIntent",
- R.raw.config_dynamic_safety_source_hidden_with_intent,
- "Element dynamic-safety-source invalid",
- "Prohibited attribute intentAction present"
- ),
+ "Duplicate id id among safety sources"),
Params(
"ConfigDynamicSafetySourceHiddenWithSearchNoTitle",
R.raw.config_dynamic_safety_source_hidden_with_search_no_title,
"Element dynamic-safety-source invalid",
- "Required attribute title missing"
- ),
- Params(
- "ConfigDynamicSafetySourceHiddenWithSummary",
- R.raw.config_dynamic_safety_source_hidden_with_summary,
- "Element dynamic-safety-source invalid",
- "Prohibited attribute summary present"
- ),
- Params(
- "ConfigDynamicSafetySourceHiddenWithTitle",
- R.raw.config_dynamic_safety_source_hidden_with_title,
- "Element dynamic-safety-source invalid",
- "Prohibited attribute title present"
- ),
+ "Required attribute title missing"),
Params(
"ConfigDynamicSafetySourceInvalidDisplay",
R.raw.config_dynamic_safety_source_invalid_display,
- "Attribute dynamic-safety-source.initialDisplayState invalid",
- null
- ),
+ "Attribute value \"invalid\" in dynamic-safety-source.initialDisplayState " +
+ "invalid",
+ null),
Params(
"ConfigDynamicSafetySourceInvalidProfile",
R.raw.config_dynamic_safety_source_invalid_profile,
- "Attribute dynamic-safety-source.profile invalid",
- null
- ),
+ "Attribute value \"invalid\" in dynamic-safety-source.profile invalid",
+ null),
Params(
"ConfigDynamicSafetySourceNoId",
R.raw.config_dynamic_safety_source_no_id,
"Element dynamic-safety-source invalid",
- "Required attribute id missing"
- ),
+ "Required attribute id missing"),
Params(
"ConfigDynamicSafetySourceNoIntent",
R.raw.config_dynamic_safety_source_no_intent,
"Element dynamic-safety-source invalid",
- "Required attribute intentAction missing"
- ),
+ "Required attribute intentAction missing"),
Params(
"ConfigDynamicSafetySourceNoPackage",
R.raw.config_dynamic_safety_source_no_package,
"Element dynamic-safety-source invalid",
- "Required attribute packageName missing"
- ),
+ "Required attribute packageName missing"),
Params(
"ConfigDynamicSafetySourceNoProfile",
R.raw.config_dynamic_safety_source_no_profile,
"Element dynamic-safety-source invalid",
- "Required attribute profile missing"
- ),
+ "Required attribute profile missing"),
Params(
"ConfigDynamicSafetySourceNoSummary",
R.raw.config_dynamic_safety_source_no_summary,
"Element dynamic-safety-source invalid",
- "Required attribute summary missing"
- ),
+ "Required attribute summary missing"),
Params(
"ConfigDynamicSafetySourceNoTitle",
R.raw.config_dynamic_safety_source_no_title,
"Element dynamic-safety-source invalid",
- "Required attribute title missing"
- ),
+ "Required attribute title missing"),
Params(
"ConfigDynamicSafetySourcePrimaryHiddenWithWork",
R.raw.config_dynamic_safety_source_primary_hidden_with_work,
"Element dynamic-safety-source invalid",
- "Prohibited attribute titleForWork present"
- ),
+ "Prohibited attribute titleForWork present"),
Params(
"ConfigDynamicSafetySourcePrimaryWithWork",
R.raw.config_dynamic_safety_source_primary_with_work,
"Element dynamic-safety-source invalid",
- "Prohibited attribute titleForWork present"
- ),
+ "Prohibited attribute titleForWork present"),
Params(
"ConfigFileCorrupted",
R.raw.config_file_corrupted,
"Exception while parsing the XML resource",
- null
- ),
+ null),
Params(
"ConfigIssueOnlySafetySourceDuplicateKey",
R.raw.config_issue_only_safety_source_duplicate_key,
"Element safety-sources-config invalid",
- "Duplicate id id among safety sources"
- ),
+ "Duplicate id id among safety sources"),
Params(
"ConfigIssueOnlySafetySourceInvalidProfile",
R.raw.config_issue_only_safety_source_invalid_profile,
- "Attribute issue-only-safety-source.profile invalid",
- null
- ),
+ "Attribute value \"invalid\" in issue-only-safety-source.profile invalid",
+ null),
Params(
"ConfigIssueOnlySafetySourceNoId",
R.raw.config_issue_only_safety_source_no_id,
"Element issue-only-safety-source invalid",
- "Required attribute id missing"
- ),
+ "Required attribute id missing"),
Params(
"ConfigIssueOnlySafetySourceNoPackage",
R.raw.config_issue_only_safety_source_no_package,
"Element issue-only-safety-source invalid",
- "Required attribute packageName missing"
- ),
+ "Required attribute packageName missing"),
Params(
"ConfigIssueOnlySafetySourceNoProfile",
R.raw.config_issue_only_safety_source_no_profile,
"Element issue-only-safety-source invalid",
- "Required attribute profile missing"
- ),
+ "Required attribute profile missing"),
Params(
"ConfigIssueOnlySafetySourceWithDisplay",
R.raw.config_issue_only_safety_source_with_display,
"Element issue-only-safety-source invalid",
- "Prohibited attribute initialDisplayState present"
- ),
+ "Prohibited attribute initialDisplayState present"),
Params(
"ConfigIssueOnlySafetySourceWithIntent",
R.raw.config_issue_only_safety_source_with_intent,
"Element issue-only-safety-source invalid",
- "Prohibited attribute intentAction present"
- ),
+ "Prohibited attribute intentAction present"),
Params(
"ConfigIssueOnlySafetySourceWithSearch",
R.raw.config_issue_only_safety_source_with_search,
"Element issue-only-safety-source invalid",
- "Prohibited attribute searchTerms present"
- ),
+ "Prohibited attribute searchTerms present"),
Params(
"ConfigIssueOnlySafetySourceWithSummary",
R.raw.config_issue_only_safety_source_with_summary,
"Element issue-only-safety-source invalid",
- "Prohibited attribute summary present"
- ),
+ "Prohibited attribute summary present"),
Params(
"ConfigIssueOnlySafetySourceWithTitle",
R.raw.config_issue_only_safety_source_with_title,
"Element issue-only-safety-source invalid",
- "Prohibited attribute title present"
- ),
+ "Prohibited attribute title present"),
Params(
"ConfigIssueOnlySafetySourceWithWork",
R.raw.config_issue_only_safety_source_with_work,
"Element issue-only-safety-source invalid",
- "Prohibited attribute titleForWork present"
- ),
+ "Prohibited attribute titleForWork present"),
Params(
"ConfigMixedSafetySourceDuplicateKey",
R.raw.config_mixed_safety_source_duplicate_key,
"Element safety-sources-config invalid",
- "Duplicate id id among safety sources"
- ),
+ "Duplicate id id among safety sources"),
Params(
"ConfigSafetyCenterConfigMissing",
R.raw.config_safety_center_config_missing,
"Element safety-center-config missing",
- null
- ),
+ null),
Params(
"ConfigSafetySourcesConfigEmpty",
R.raw.config_safety_sources_config_empty,
"Element safety-sources-config invalid",
- "No safety sources groups present"
- ),
+ "No safety sources groups present"),
Params(
"ConfigSafetySourcesConfigMissing",
R.raw.config_safety_sources_config_missing,
"Element safety-sources-config missing",
- null
- ),
+ null),
Params(
"ConfigSafetySourcesGroupDuplicateId",
R.raw.config_safety_sources_group_duplicate_id,
"Element safety-sources-config invalid",
- "Duplicate id id among safety sources groups"
- ),
+ "Duplicate id id among safety sources groups"),
Params(
"ConfigSafetySourcesGroupEmpty",
R.raw.config_safety_sources_group_empty,
"Element safety-sources-group invalid",
- "Safety sources group empty"
- ),
+ "Safety sources group empty"),
Params(
"ConfigSafetySourcesGroupInvalidIcon",
R.raw.config_safety_sources_group_invalid_icon,
- "Attribute safety-sources-group.statelessIconType invalid",
- null
- ),
+ "Attribute value \"invalid\" in safety-sources-group.statelessIconType invalid",
+ null),
Params(
"ConfigSafetySourcesGroupNoId",
R.raw.config_safety_sources_group_no_id,
"Element safety-sources-group invalid",
- "Required attribute id missing"
- ),
+ "Required attribute id missing"),
Params(
"ConfigSafetySourcesGroupNoTitle",
R.raw.config_safety_sources_group_no_title,
"Element safety-sources-group invalid",
- "Required attribute title missing"
- ),
+ "Required attribute title missing"),
Params(
"ConfigStaticSafetySourceDuplicateKey",
R.raw.config_static_safety_source_duplicate_key,
"Element safety-sources-config invalid",
- "Duplicate id id among safety sources"
- ),
+ "Duplicate id id among safety sources"),
Params(
"ConfigStaticSafetySourceInvalidProfile",
R.raw.config_static_safety_source_invalid_profile,
- "Attribute static-safety-source.profile invalid",
- null
- ),
+ "Attribute value \"invalid\" in static-safety-source.profile invalid",
+ null),
Params(
"ConfigStaticSafetySourceNoId",
R.raw.config_static_safety_source_no_id,
"Element static-safety-source invalid",
- "Required attribute id missing"
- ),
+ "Required attribute id missing"),
Params(
"ConfigStaticSafetySourceNoIntent",
R.raw.config_static_safety_source_no_intent,
"Element static-safety-source invalid",
- "Required attribute intentAction missing"
- ),
+ "Required attribute intentAction missing"),
Params(
"ConfigStaticSafetySourceNoProfile",
R.raw.config_static_safety_source_no_profile,
"Element static-safety-source invalid",
- "Required attribute profile missing"
- ),
+ "Required attribute profile missing"),
Params(
"ConfigStaticSafetySourceNoTitle",
R.raw.config_static_safety_source_no_title,
"Element static-safety-source invalid",
- "Required attribute title missing"
- ),
+ "Required attribute title missing"),
Params(
"ConfigStaticSafetySourceWithDisplay",
R.raw.config_static_safety_source_with_display,
"Element static-safety-source invalid",
- "Prohibited attribute initialDisplayState present"
- ),
+ "Prohibited attribute initialDisplayState present"),
Params(
"ConfigStaticSafetySourceWithLogging",
R.raw.config_static_safety_source_with_logging,
"Element static-safety-source invalid",
- "Prohibited attribute loggingAllowed present"
- ),
+ "Prohibited attribute loggingAllowed present"),
Params(
"ConfigStaticSafetySourceWithPackage",
R.raw.config_static_safety_source_with_package,
"Element static-safety-source invalid",
- "Prohibited attribute packageName present"
- ),
+ "Prohibited attribute packageName present"),
Params(
"ConfigStaticSafetySourceWithPrimaryAndWork",
R.raw.config_static_safety_source_with_primary_and_work,
"Element static-safety-source invalid",
- "Prohibited attribute titleForWork present"
- ),
+ "Prohibited attribute titleForWork present"),
Params(
"ConfigStaticSafetySourceWithRefresh",
R.raw.config_static_safety_source_with_refresh,
"Element static-safety-source invalid",
- "Prohibited attribute refreshOnPageOpenAllowed present"
- ),
+ "Prohibited attribute refreshOnPageOpenAllowed present"),
Params(
"ConfigStaticSafetySourceWithSeverity",
R.raw.config_static_safety_source_with_severity,
"Element static-safety-source invalid",
- "Prohibited attribute maxSeverityLevel present"
- ),
+ "Prohibited attribute maxSeverityLevel present"),
Params(
"ConfigStringResourceNameInvalidEmpty",
R.raw.config_string_resource_name_empty,
"Resource name in safety-sources-group.title cannot be empty",
- null
- ),
+ null),
Params(
"ConfigStringResourceNameInvalidNoAt",
R.raw.config_string_resource_name_invalid_no_at,
- "Resource name com.android.safetycenter.config.tests:string/reference in " +
+ "Resource name \"com.android.safetycenter.config.tests:string/reference\" in " +
"safety-sources-group.title does not start with @",
- null
- ),
+ null),
Params(
"ConfigStringResourceNameInvalidNoPackage",
R.raw.config_string_resource_name_invalid_no_package,
- "Resource name @string/reference in safety-sources-group.title does not " +
+ "Resource name \"@string/reference\" in safety-sources-group.title does not " +
"specify a package",
- null
- ),
+ null),
Params(
"ConfigStringResourceNameInvalidNoType",
R.raw.config_string_resource_name_invalid_no_type,
- "Resource name @com.android.safetycenter.config.tests:reference in " +
+ "Resource name \"@com.android.safetycenter.config.tests:reference\" in " +
"safety-sources-group.title does not specify a type",
- null
- ),
+ null),
Params(
"ConfigStringResourceNameInvalidWrongType",
R.raw.config_string_resource_name_invalid_wrong_type,
- "Resource name @com.android.safetycenter.config.tests:raw/" +
- "config_string_resource_name_invalid_wrong_type in " +
+ "Resource name \"@com.android.safetycenter.config.tests:raw/" +
+ "config_string_resource_name_invalid_wrong_type\" in " +
"safety-sources-group.title is not a string",
- null
- ),
+ null),
Params(
"ConfigStringResourceNameMissing",
R.raw.config_string_resource_name_missing,
- "Resource name @com.android.safetycenter.config.tests:string/missing in " +
+ "Resource name \"@com.android.safetycenter.config.tests:string/missing\" in " +
"safety-sources-group.title missing or invalid",
- null
- )
- )
+ null))
}
}
diff --git a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigOverlayTest.kt b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigOverlayTest.kt
index 26dd3dbcf..a78d8e502 100644
--- a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigOverlayTest.kt
+++ b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigOverlayTest.kt
@@ -22,13 +22,13 @@ import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
import com.android.compatibility.common.util.SystemUtil.runShellCommand
+import com.android.safetycenter.config.Coroutines.waitForTestToPass
import com.android.safetycenter.config.Coroutines.waitForWithTimeout
import com.android.safetycenter.config.tests.R
-import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
-import org.junit.After
+import org.junit.AfterClass
import org.junit.Assert.assertThrows
-import org.junit.Before
+import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,21 +37,8 @@ import org.junit.runner.RunWith
class ParserConfigOverlayTest {
private val context: Context = getApplicationContext()
- @Before
- fun install() {
- runShellCommand("pm install -r --force-sdk --force-queryable $OVERLAY_PATH")
- waitForWithTimeout { getStateForOverlay(OVERLAY_PACKAGE) == STATE_DISABLED }
- runShellCommand("cmd overlay enable --user 0 $OVERLAY_PACKAGE")
- waitForWithTimeout { getStateForOverlay(OVERLAY_PACKAGE) == STATE_ENABLED }
- }
-
- @After
- fun uninstall() {
- runShellCommand("pm uninstall $OVERLAY_PACKAGE")
- }
-
@Test
- fun validNotOverlayableConfig_matchesExpected() {
+ fun validNotOverlayableConfig_matchesExpected() = waitForTestToPass {
val inputStream = context.resources.openRawResource(R.raw.config_valid_not_overlayable)
val safetyCenterConfig =
@@ -85,7 +72,7 @@ class ParserConfigOverlayTest {
}
@Test
- fun validOverlayableConfig_matchesExpected() {
+ fun validOverlayableConfig_matchesExpected() = waitForTestToPass {
val inputStream = context.resources.openRawResource(R.raw.config_valid_overlayable)
val safetyCenterConfig =
@@ -116,19 +103,20 @@ class ParserConfigOverlayTest {
}
@Test
- fun invalidOverlayableConfig_StringResourceNameInvalid_throws() {
- val inputStream = context.resources.openRawResource(
- R.raw.config_string_resource_name_invalid_overlayable
- )
+ fun invalidOverlayableConfig_StringResourceNameInvalid_throws() = waitForTestToPass {
+ val inputStream =
+ context.resources.openRawResource(R.raw.config_string_resource_name_invalid_overlayable)
- val thrown = assertThrows(ParseException::class.java) {
- SafetyCenterConfigParser.parseXmlResource(inputStream, context.resources)
- }
+ val thrown =
+ assertThrows(ParseException::class.java) {
+ SafetyCenterConfigParser.parseXmlResource(inputStream, context.resources)
+ }
- assertThat(thrown).hasMessageThat().isEqualTo(
- "Resource name @com.android.safetycenter.config.tests:string/reference_overlay in " +
- "static-safety-source.summary missing or invalid"
- )
+ assertThat(thrown)
+ .hasMessageThat()
+ .isEqualTo(
+ "Resource name \"@com.android.safetycenter.config.tests:string/reference_overlay" +
+ "\" in static-safety-source.summary missing or invalid")
}
companion object {
@@ -136,25 +124,29 @@ class ParserConfigOverlayTest {
private const val OVERLAY_PACKAGE = "com.android.safetycenter.config.tests.overlay"
private const val OVERLAY_PATH =
"/data/local/tmp/com/safetycenter/config/tests/SafetyCenterConfigTestsOverlay.apk"
- private const val OVERLAY_WAIT_TIMEOUT_MILLIS = 10000
private const val STATE_ENABLED = "STATE_ENABLED"
- private const val STATE_DISABLED = "STATE_DISABLED"
private fun getStateForOverlay(overlayPackage: String): String? {
- val result: String = runShellCommand("cmd overlay dump")
- val startIndex = result.indexOf("$overlayPackage:0")
- if (startIndex < 0) {
+ val result: String = runShellCommand("cmd overlay dump --user 0 state $overlayPackage")
+ if (!result.startsWith("STATE_")) {
return null
}
- val endIndex = result.indexOf('}', startIndex)
- assertThat(endIndex).isGreaterThan(startIndex)
- val stateIndex = result.indexOf("mState", startIndex)
- assertThat(stateIndex).isIn(Range.open(startIndex, endIndex))
- val colonIndex = result.indexOf(':', stateIndex)
- assertThat(colonIndex).isIn(Range.open(stateIndex, endIndex))
- val endLineIndex = result.indexOf('\n', colonIndex)
- assertThat(endLineIndex).isIn(Range.open(colonIndex, endIndex))
- return result.substring(colonIndex + 2, endLineIndex)
+ return result.trim()
+ }
+
+ @JvmStatic
+ @BeforeClass
+ fun install() {
+ runShellCommand("pm install -r --force-sdk --force-queryable $OVERLAY_PATH")
+ waitForWithTimeout { getStateForOverlay(OVERLAY_PACKAGE) != null }
+ runShellCommand("cmd overlay enable --user 0 $OVERLAY_PACKAGE")
+ waitForWithTimeout { getStateForOverlay(OVERLAY_PACKAGE) == STATE_ENABLED }
+ }
+
+ @JvmStatic
+ @AfterClass
+ fun uninstall() {
+ runShellCommand("pm uninstall $OVERLAY_PACKAGE")
}
}
}
diff --git a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt
index 0effff7ba..dcb6d9cd2 100644
--- a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt
+++ b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt
@@ -76,6 +76,22 @@ class ParserConfigValidTest {
)
.addSafetySource(
SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+ .setId("dynamic_all_references")
+ .setPackageName("package")
+ .setTitleResId(R.string.reference)
+ .setTitleForWorkResId(R.string.reference)
+ .setSummaryResId(R.string.reference)
+ .setIntentAction("intent")
+ .setProfile(SafetySource.PROFILE_ALL)
+ .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+ .setMaxSeverityLevel(300)
+ .setSearchTermsResId(R.string.reference)
+ .setLoggingAllowed(false)
+ .setRefreshOnPageOpenAllowed(true)
+ .build()
+ )
+ .addSafetySource(
+ SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
.setId("dynamic_disabled")
.setPackageName("package")
.setTitleResId(R.string.reference)
@@ -98,6 +114,8 @@ class ParserConfigValidTest {
.setPackageName("package")
.setTitleResId(R.string.reference)
.setTitleForWorkResId(R.string.reference)
+ .setSummaryResId(R.string.reference)
+ .setIntentAction("intent")
.setProfile(SafetySource.PROFILE_ALL)
.setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN)
.setSearchTermsResId(R.string.reference)
diff --git a/SafetyCenter/Config/tests/res/raw/config_dynamic_safety_source_hidden_with_intent.xml b/SafetyCenter/Config/tests/res/raw/config_dynamic_safety_source_hidden_with_intent.xml
deleted file mode 100644
index faee1c9e1..000000000
--- a/SafetyCenter/Config/tests/res/raw/config_dynamic_safety_source_hidden_with_intent.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<safety-center-config>
- <safety-sources-config>
- <safety-sources-group
- id="id"
- title="@com.android.safetycenter.config.tests:string/reference"
- summary="@com.android.safetycenter.config.tests:string/reference">
- <dynamic-safety-source
- id="id"
- packageName="package"
- intentAction="intent"
- profile="primary_profile_only"
- initialDisplayState="hidden"/>
- </safety-sources-group>
- </safety-sources-config>
-</safety-center-config>
diff --git a/SafetyCenter/Config/tests/res/raw/config_dynamic_safety_source_hidden_with_summary.xml b/SafetyCenter/Config/tests/res/raw/config_dynamic_safety_source_hidden_with_summary.xml
deleted file mode 100644
index 9e9384350..000000000
--- a/SafetyCenter/Config/tests/res/raw/config_dynamic_safety_source_hidden_with_summary.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<safety-center-config>
- <safety-sources-config>
- <safety-sources-group
- id="id"
- title="@com.android.safetycenter.config.tests:string/reference"
- summary="@com.android.safetycenter.config.tests:string/reference">
- <dynamic-safety-source
- id="id"
- packageName="package"
- summary="@com.android.safetycenter.config.tests:string/reference"
- profile="primary_profile_only"
- initialDisplayState="hidden"/>
- </safety-sources-group>
- </safety-sources-config>
-</safety-center-config>
diff --git a/SafetyCenter/Config/tests/res/raw/config_dynamic_safety_source_hidden_with_title.xml b/SafetyCenter/Config/tests/res/raw/config_dynamic_safety_source_hidden_with_title.xml
deleted file mode 100644
index a79f438f7..000000000
--- a/SafetyCenter/Config/tests/res/raw/config_dynamic_safety_source_hidden_with_title.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<safety-center-config>
- <safety-sources-config>
- <safety-sources-group
- id="id"
- title="@com.android.safetycenter.config.tests:string/reference"
- summary="@com.android.safetycenter.config.tests:string/reference">
- <dynamic-safety-source
- id="id"
- packageName="package"
- title="@com.android.safetycenter.config.tests:string/reference"
- profile="primary_profile_only"
- initialDisplayState="hidden"/>
- </safety-sources-group>
- </safety-sources-config>
-</safety-center-config>
diff --git a/SafetyCenter/Config/tests/res/raw/config_valid.xml b/SafetyCenter/Config/tests/res/raw/config_valid.xml
index 03708b818..8a3ee0917 100644
--- a/SafetyCenter/Config/tests/res/raw/config_valid.xml
+++ b/SafetyCenter/Config/tests/res/raw/config_valid.xml
@@ -26,6 +26,19 @@
loggingAllowed="false"
refreshOnPageOpenAllowed="true"/>
<dynamic-safety-source
+ id="@com.android.safetycenter.config.tests:string/dynamic_all_references_id"
+ packageName="@com.android.safetycenter.config.tests:string/dynamic_all_references_package_name"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ titleForWork="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="@com.android.safetycenter.config.tests:string/dynamic_all_references_intent_action"
+ profile="@com.android.safetycenter.config.tests:string/dynamic_all_references_profile"
+ initialDisplayState="@com.android.safetycenter.config.tests:string/dynamic_all_references_initial_display_state"
+ maxSeverityLevel="@com.android.safetycenter.config.tests:string/dynamic_all_references_max_severity_level"
+ searchTerms="@com.android.safetycenter.config.tests:string/reference"
+ loggingAllowed="@com.android.safetycenter.config.tests:string/dynamic_all_references_logging_allowed"
+ refreshOnPageOpenAllowed="@com.android.safetycenter.config.tests:string/dynamic_all_references_refresh_on_page_open_allowed"/>
+ <dynamic-safety-source
id="dynamic_disabled"
packageName="package"
title="@com.android.safetycenter.config.tests:string/reference"
@@ -42,6 +55,8 @@
packageName="package"
title="@com.android.safetycenter.config.tests:string/reference"
titleForWork="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
profile="all_profiles"
initialDisplayState="hidden"
searchTerms="@com.android.safetycenter.config.tests:string/reference"/>
diff --git a/SafetyCenter/Config/tests/res/values/strings.xml b/SafetyCenter/Config/tests/res/values/strings.xml
index 0f2303648..195f56c2a 100644
--- a/SafetyCenter/Config/tests/res/values/strings.xml
+++ b/SafetyCenter/Config/tests/res/values/strings.xml
@@ -15,8 +15,15 @@
~ limitations under the License.
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Test reference -->
<string name="reference" translatable="false">Reference</string>
<string name="reference_overlayable" translatable="false">Base</string>
<string name="reference_not_overlayable" translatable="false">Base</string>
+ <string name="dynamic_all_references_id" translatable="false">dynamic_all_references</string>
+ <string name="dynamic_all_references_package_name" translatable="false">package</string>
+ <string name="dynamic_all_references_intent_action" translatable="false">intent</string>
+ <string name="dynamic_all_references_profile" translatable="false">all_profiles</string>
+ <string name="dynamic_all_references_initial_display_state" translatable="false">disabled</string>
+ <string name="dynamic_all_references_max_severity_level" translatable="false">300</string>
+ <string name="dynamic_all_references_logging_allowed" translatable="false">false</string>
+ <string name="dynamic_all_references_refresh_on_page_open_allowed" translatable="false">true</string>
</resources>
diff --git a/SafetyCenter/ConfigLintChecker/jarjar-rules.txt b/SafetyCenter/ConfigLintChecker/jarjar-rules.txt
index c4a25fe33..46d2cfe4d 100644
--- a/SafetyCenter/ConfigLintChecker/jarjar-rules.txt
+++ b/SafetyCenter/ConfigLintChecker/jarjar-rules.txt
@@ -7,4 +7,5 @@
# reference by `LintJarVerifier`. To work around this, preserve the dynamically linked Android Lint
# API references and rename any other `com.android` reference.
rule com.android.tools.lint.** @0
+rule com.android.resources.ResourceFolderType @0
rule com.android.** android.safetycenter.lint.jarjar.@0
diff --git a/SafetyCenter/ConfigLintChecker/java/android/content/res/Resources.java b/SafetyCenter/ConfigLintChecker/java/android/content/res/Resources.java
index 8dff0a4ae..dcf60b204 100644
--- a/SafetyCenter/ConfigLintChecker/java/android/content/res/Resources.java
+++ b/SafetyCenter/ConfigLintChecker/java/android/content/res/Resources.java
@@ -16,16 +16,49 @@
package android.content.res;
+import java.util.Map;
+
/** Stub class to compile the linter for host execution. */
public final class Resources {
/** Constant used in the Safety Center config parser. */
public static final int ID_NULL = 0;
+ private static final String STRING_TYPE = "string";
+
+ private final String mPackageName;
+ private final Map<String, Integer> mNameToIndex;
+ private final Map<Integer, String> mIndexToValue;
+
/** Class used in the Safety Center config parser. */
- public Resources() {}
+ public Resources(
+ String packageName,
+ Map<String, Integer> nameToIndex,
+ Map<Integer, String> indexToValue) {
+ mPackageName = packageName;
+ mNameToIndex = nameToIndex;
+ mIndexToValue = indexToValue;
+ }
+
+ /** This exception is thrown by the resource APIs when a requested resource can not be found. */
+ public static final class NotFoundException extends RuntimeException {
+ public NotFoundException() {}
+ }
/** Method used in the Safety Center config parser. */
public int getIdentifier(String name, String defType, String defPackage) {
- return ID_NULL + 1;
+ if (!mPackageName.equals(defPackage)
+ || !STRING_TYPE.equals(defType)
+ || !mNameToIndex.containsKey(name)) {
+ return ID_NULL;
+ }
+ return mNameToIndex.get(name);
+ }
+
+ /** Method used in the Safety Center config parser. */
+ public String getString(int id) {
+ if (mIndexToValue.containsKey(id)) {
+ return mIndexToValue.get(id);
+ }
+ throw new NotFoundException();
}
}
diff --git a/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt
index f9cb62a8f..e512b7e54 100644
--- a/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt
+++ b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt
@@ -17,6 +17,8 @@
package android.safetycenter.lint
import android.content.res.Resources
+import com.android.SdkConstants.ATTR_NAME
+import com.android.SdkConstants.TAG_STRING
import com.android.resources.ResourceFolderType
import com.android.safetycenter.config.ParseException
import com.android.safetycenter.config.SafetyCenterConfigParser
@@ -29,43 +31,93 @@ import com.android.tools.lint.detector.api.Location
import com.android.tools.lint.detector.api.OtherFileScanner
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.XmlContext
+import com.android.tools.lint.detector.api.XmlScanner
+import java.util.EnumSet
+import org.w3c.dom.Element
+import org.w3c.dom.Node
/** Lint check for detecting invalid Safety Center configs */
-class ParserExceptionDetector : Detector(), OtherFileScanner {
+class ParserExceptionDetector : Detector(), OtherFileScanner, XmlScanner {
companion object {
- val ISSUE = Issue.create(
- id = "InvalidSafetyCenterConfig",
- briefDescription = "The Safety Center config parser detected an error",
- explanation = """The Safety Center config must follow all constraints defined in \
+ val ISSUE =
+ Issue.create(
+ id = "InvalidSafetyCenterConfig",
+ briefDescription = "The Safety Center config parser detected an error",
+ explanation =
+ """The Safety Center config must follow all constraints defined in \
safety_center_config.xsd. Check the error message to find out the specific \
constraint not met by the current config.""",
- category = Category.CORRECTNESS,
- severity = Severity.ERROR,
- implementation = Implementation(
- ParserExceptionDetector::class.java,
- Scope.OTHER_SCOPE
- ),
- androidSpecific = true
- )
+ category = Category.CORRECTNESS,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(
+ ParserExceptionDetector::class.java,
+ EnumSet.of(Scope.RESOURCE_FILE, Scope.OTHER)),
+ androidSpecific = true)
+
+ val STRING_MAP_BUILD_PHASE = 1
+ val CONFIG_PARSE_PHASE = 2
}
override fun appliesTo(folderType: ResourceFolderType): Boolean {
- return folderType == ResourceFolderType.RAW
+ return folderType == ResourceFolderType.RAW || folderType == ResourceFolderType.VALUES
+ }
+
+ override fun afterCheckEachProject(context: Context) {
+ context.driver.requestRepeat(this, Scope.OTHER_SCOPE)
+ }
+
+ /** Implements XmlScanner and builds a map of string resources in the first phase */
+ val mNameToIndex: MutableMap<String, Int> = mutableMapOf()
+ val mIndexToValue: MutableMap<Int, String> = mutableMapOf()
+ var mIndex = 1000
+
+ override fun getApplicableElements(): Collection<String>? {
+ return listOf(TAG_STRING)
+ }
+
+ override fun visitElement(context: XmlContext, element: Element) {
+ if (context.driver.phase != STRING_MAP_BUILD_PHASE ||
+ context.resourceFolderType != ResourceFolderType.VALUES) {
+ return
+ }
+ val name = element.getAttribute(ATTR_NAME)
+ var value = ""
+ for (index in 0 until element.childNodes.length) {
+ val child = element.childNodes.item(index)
+ if (child.nodeType == Node.TEXT_NODE) {
+ value = child.nodeValue
+ break
+ }
+ }
+ mNameToIndex[name] = mIndex
+ mIndexToValue[mIndex] = value
+ mIndex++
}
+ /** Implements OtherFileScanner and parses the XML config in the second phase */
override fun run(context: Context) {
- if (context.file.name != "safety_center_config.xml") {
+ if (context.driver.phase != CONFIG_PARSE_PHASE ||
+ context.file.name != "safety_center_config.xml") {
return
}
try {
- SafetyCenterConfigParser.parseXmlResource(context.file.inputStream(), Resources())
+ SafetyCenterConfigParser.parseXmlResource(
+ context.file.inputStream(),
+ // Note: using a map of the string resources present in the APK under analysis is
+ // necessary in order to get the value of string resources that are resolved and
+ // validated at parse time. The drawback of this is that the linter cannot be used
+ // on overlay packages that refer to resources in the target package or on packages
+ // that refer to Android global resources. However, we cannot use custom a linter
+ // with the default soong overlay build rule regardless.
+ Resources(context.project.`package`, mNameToIndex, mIndexToValue))
} catch (e: ParseException) {
context.report(
ISSUE,
Location.create(context.file),
- "Parser exception: \"${e.message}\", cause: \"${e.cause?.message}\""
- )
+ "Parser exception: \"${e.message}\", cause: \"${e.cause?.message}\"")
}
}
-} \ No newline at end of file
+}
diff --git a/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt b/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt
index 8c859ae1d..ad7d36685 100644
--- a/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt
+++ b/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt
@@ -36,44 +36,59 @@ class ParserExceptionDetectorTest : LintDetectorTest() {
@Test
fun validConfig_doesNotThrow() {
- lint().files((
- xml("res/raw/safety_center_config.xml",
- """
+ lint()
+ .files(
+ (xml(
+ "res/raw/safety_center_config.xml",
+ """
<safety-center-config>
<safety-sources-config>
<safety-sources-group
id="group"
- title="@package:string/reference"
- summary="@package:string/reference">
+ title="@lint.test.pkg:string/reference"
+ summary="@lint.test.pkg:string/reference">
<static-safety-source
id="source"
- title="@package:string/reference"
- summary="@package:string/reference"
+ title="@lint.test.pkg:string/reference"
+ summary="@lint.test.pkg:string/reference"
intentAction="intent"
profile="primary_profile_only"/>
</safety-sources-group>
</safety-sources-config>
</safety-center-config>
- """))).run().expectClean()
+ """)),
+ (xml(
+ "res/values/strings.xml",
+ """
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="reference" translatable="false">Reference</string>
+</resources>
+ """)))
+ .run()
+ .expectClean()
}
@Test
fun invalidConfig_throws() {
- lint().files((xml("res/raw/safety_center_config.xml", "<invalid-root/>")))
- .run().expect("res/raw/safety_center_config.xml: Error: Parser exception: " +
- "\"Element safety-center-config missing\", cause: \"null\" " +
- "[InvalidSafetyCenterConfig]\n1 errors, 0 warnings")
+ lint()
+ .files((xml("res/raw/safety_center_config.xml", "<invalid-root/>")))
+ .run()
+ .expect(
+ "res/raw/safety_center_config.xml: Error: Parser exception: " +
+ "\"Element safety-center-config missing\", cause: \"null\" " +
+ "[InvalidSafetyCenterConfig]\n1 errors, 0 warnings")
}
@Test
fun unrelatedFile_doesNotThrow() {
- lint().files((xml("res/raw/some_other_config.xml", "<some-other-root/>")))
- .run().expectClean()
+ lint()
+ .files((xml("res/raw/some_other_config.xml", "<some-other-root/>")))
+ .run()
+ .expectClean()
}
@Test
fun unrelatedFolder_doesNotThrow() {
- lint().files((xml("res/values/strings.xml", "<some-other-root/>")))
- .run().expectClean()
+ lint().files((xml("res/values/strings.xml", "<some-other-root/>"))).run().expectClean()
}
}
diff --git a/framework-s/java/android/safetycenter/config/SafetySource.java b/framework-s/java/android/safetycenter/config/SafetySource.java
index 7caa5439b..ab4a8929b 100644
--- a/framework-s/java/android/safetycenter/config/SafetySource.java
+++ b/framework-s/java/android/safetycenter/config/SafetySource.java
@@ -588,36 +588,33 @@ public final class SafetySource implements Parcelable {
isIssueOnly);
boolean isDynamicHiddenWithSearch =
isDynamic && isHidden && searchTermsResId != Resources.ID_NULL;
- boolean isDynamicHiddenWithoutSearch =
- isDynamic && isHidden && searchTermsResId == Resources.ID_NULL;
boolean titleRequired = isDynamicNotHidden || isDynamicHiddenWithSearch || isStatic;
- boolean titleProhibited = isIssueOnly || isDynamicHiddenWithoutSearch;
int titleResId =
BuilderUtils.validateResId(
mTitleResId, "title",
titleRequired,
- titleProhibited);
+ isIssueOnly);
int titleForWorkResId =
BuilderUtils.validateResId(
mTitleForWorkResId,
"titleForWork",
hasWork && titleRequired,
- !hasWork || titleProhibited);
+ !hasWork || isIssueOnly);
int summaryResId =
BuilderUtils.validateResId(
mSummaryResId,
"summary",
isDynamicNotHidden,
- isIssueOnly || isHidden);
+ isIssueOnly);
BuilderUtils.validateAttribute(
mIntentAction,
"intentAction",
(isDynamic && isEnabled) || isStatic,
- isIssueOnly || isHidden);
+ isIssueOnly);
int maxSeverityLevel =
BuilderUtils.validateInteger(
diff --git a/framework-s/java/android/safetycenter/config/TEST_MAPPING b/framework-s/java/android/safetycenter/config/TEST_MAPPING
new file mode 100644
index 000000000..a39176e27
--- /dev/null
+++ b/framework-s/java/android/safetycenter/config/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "SafetyCenterConfigTests"
+ }
+ ]
+}
diff --git a/framework-s/java/android/safetycenter/config/safety_center_config.xsd b/framework-s/java/android/safetycenter/config/safety_center_config.xsd
index 1147b16e6..8549df80f 100644
--- a/framework-s/java/android/safetycenter/config/safety_center_config.xsd
+++ b/framework-s/java/android/safetycenter/config/safety_center_config.xsd
@@ -41,61 +41,85 @@
<xsd:element name="issue-only-safety-source" type="issue-only-safety-source"/>
</xsd:choice>
<!-- id must be unique among safety sources groups -->
- <xsd:attribute name="id" type="xsd:string" use="required"/>
+ <xsd:attribute name="id" type="stringOrStringResourceName" use="required"/>
<!-- title is required unless the group contains issue only and/or internal sources -->
- <xsd:attribute name="title" type="stringResourceName"/>
- <xsd:attribute name="summary" type="stringResourceName"/>
- <xsd:attribute name="statelessIconType" type="statelessIconType" default="none"/>
+ <xsd:attribute name="title" type="runtimeStringResourceName"/>
+ <xsd:attribute name="summary" type="runtimeStringResourceName"/>
+ <xsd:attribute name="statelessIconType" type="statelessIconTypeOrStringResourceName" default="none"/>
</xsd:complexType>
<xsd:complexType name="dynamic-safety-source">
<!-- id must be unique among safety sources -->
- <xsd:attribute name="id" type="xsd:string" use="required"/>
- <xsd:attribute name="packageName" type="xsd:string" use="required"/>
+ <xsd:attribute name="id" type="stringOrStringResourceName" use="required"/>
+ <xsd:attribute name="packageName" type="stringOrStringResourceName" use="required"/>
<!-- title is required if initialDisplayState is not set to hidden or if searchTerms are provided -->
- <!-- title is prohibited if initialDisplayState is set to hidden and if searchTerms are not provided -->
- <xsd:attribute name="title" type="stringResourceName"/>
+ <xsd:attribute name="title" type="runtimeStringResourceName"/>
<!-- titleForWork is required if profile is set to all_profiles, and initialDisplayState is not set to hidden or if searchTerms are provided -->
- <!-- titleForWork is prohibited if profile is set to primary_profile_only, or initialDisplayState is set to hidden and if searchTerms are not provided -->
- <xsd:attribute name="titleForWork" type="stringResourceName"/>
+ <!-- titleForWork is prohibited if profile is set to primary_profile_only -->
+ <xsd:attribute name="titleForWork" type="runtimeStringResourceName"/>
<!-- summary is required if initialDisplayState is not set to hidden -->
- <!-- summary is prohibited if initialDisplayState is set to hidden -->
- <xsd:attribute name="summary" type="stringResourceName"/>
+ <xsd:attribute name="summary" type="runtimeStringResourceName"/>
<!-- intentAction is required if initialDisplayState is set to enabled -->
- <!-- intentAction is optional if initialDisplayState is set to disabled -->
- <!-- intentAction is prohibited if initialDisplayState is set to hidden -->
- <xsd:attribute name="intentAction" type="xsd:string"/>
+ <xsd:attribute name="intentAction" type="stringOrStringResourceName"/>
<xsd:attribute name="profile" type="profile" use="required"/>
- <xsd:attribute name="initialDisplayState" type="initialDisplayState" default="enabled"/>
- <xsd:attribute name="maxSeverityLevel" type="xsd:int" default="2147483647"/>
- <xsd:attribute name="searchTerms" type="stringResourceName"/>
- <xsd:attribute name="loggingAllowed" type="xsd:boolean" default="true"/>
- <xsd:attribute name="refreshOnPageOpenAllowed" type="xsd:boolean" default="false"/>
+ <xsd:attribute name="initialDisplayState" type="initialDisplayStateOrStringResourceName" default="enabled"/>
+ <xsd:attribute name="maxSeverityLevel" type="intOrStringResourceName" default="2147483647"/>
+ <xsd:attribute name="searchTerms" type="runtimeStringResourceName"/>
+ <xsd:attribute name="loggingAllowed" type="booleanOrStringResourceName" default="true"/>
+ <xsd:attribute name="refreshOnPageOpenAllowed" type="booleanOrStringResourceName" default="false"/>
</xsd:complexType>
<xsd:complexType name="issue-only-safety-source">
<!-- id must be unique among safety sources -->
- <xsd:attribute name="id" type="xsd:string" use="required"/>
- <xsd:attribute name="packageName" type="xsd:string" use="required"/>
- <xsd:attribute name="profile" type="profile" use="required"/>
- <xsd:attribute name="maxSeverityLevel" type="xsd:int" default="2147483647"/>
- <xsd:attribute name="loggingAllowed" type="xsd:boolean" default="true"/>
- <xsd:attribute name="refreshOnPageOpenAllowed" type="xsd:boolean" default="false"/>
+ <xsd:attribute name="id" type="stringOrStringResourceName" use="required"/>
+ <xsd:attribute name="packageName" type="stringOrStringResourceName" use="required"/>
+ <xsd:attribute name="profile" type="profileOrStringResourceName" use="required"/>
+ <xsd:attribute name="maxSeverityLevel" type="intOrStringResourceName" default="2147483647"/>
+ <xsd:attribute name="loggingAllowed" type="booleanOrStringResourceName" default="true"/>
+ <xsd:attribute name="refreshOnPageOpenAllowed" type="booleanOrStringResourceName" default="false"/>
</xsd:complexType>
<xsd:complexType name="static-safety-source">
<!-- id must be unique among safety sources -->
- <xsd:attribute name="id" type="xsd:string" use="required"/>
- <xsd:attribute name="title" type="stringResourceName" use="required"/>
+ <xsd:attribute name="id" type="stringOrStringResourceName" use="required"/>
+ <xsd:attribute name="title" type="runtimeStringResourceName" use="required"/>
<!-- titleForWork is required if profile is set to all_profiles -->
<!-- titleForWork is prohibited if profile is set to primary_profile_only -->
- <xsd:attribute name="titleForWork" type="stringResourceName"/>
- <xsd:attribute name="summary" type="stringResourceName"/>
- <xsd:attribute name="intentAction" type="xsd:string" use="required"/>
- <xsd:attribute name="profile" type="profile" use="required"/>
- <xsd:attribute name="searchTerms" type="stringResourceName"/>
+ <xsd:attribute name="titleForWork" type="runtimeStringResourceName"/>
+ <xsd:attribute name="summary" type="runtimeStringResourceName"/>
+ <xsd:attribute name="intentAction" type="stringOrStringResourceName" use="required"/>
+ <xsd:attribute name="profile" type="profileOrStringResourceName" use="required"/>
+ <xsd:attribute name="searchTerms" type="runtimeStringResourceName"/>
</xsd:complexType>
+ <xsd:simpleType name="intOrStringResourceName">
+ <!-- String resource names will be resolved only once at parse time. -->
+ <!-- Locale changes and device config changes will be ignored. -->
+ <!-- The value of the string resource must be of type xsd:int. -->
+ <xsd:union memberTypes="stringResourceName xsd:int" />
+ </xsd:simpleType>
+
+ <xsd:simpleType name="booleanOrStringResourceName">
+ <!-- String resource names will be resolved only once at parse time. -->
+ <!-- Locale changes and device config changes will be ignored. -->
+ <!-- The value of the string resource must be of type xsd:boolean. -->
+ <xsd:union memberTypes="stringResourceName xsd:boolean" />
+ </xsd:simpleType>
+
+ <xsd:simpleType name="stringOrStringResourceName">
+ <!-- String resource names will be resolved only once at parse time. -->
+ <!-- Locale changes and device config changes will be ignored. -->
+ <!-- The value of the string resource must be of type xsd:string. -->
+ <xsd:union memberTypes="stringResourceName xsd:string" />
+ </xsd:simpleType>
+
+ <xsd:simpleType name="statelessIconTypeOrStringResourceName">
+ <!-- String resource names will be resolved only once at parse time. -->
+ <!-- Locale changes and device config changes will be ignored. -->
+ <!-- The value of the string resource must be of type statelessIconType. -->
+ <xsd:union memberTypes="stringResourceName statelessIconType" />
+ </xsd:simpleType>
+
<xsd:simpleType name="statelessIconType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="none"/>
@@ -103,6 +127,13 @@
</xsd:restriction>
</xsd:simpleType>
+ <xsd:simpleType name="profileOrStringResourceName">
+ <!-- String resource names will be resolved only once at parse time. -->
+ <!-- Locale changes and device config changes will be ignored. -->
+ <!-- The value of the string resource must be of type profile. -->
+ <xsd:union memberTypes="stringResourceName profile" />
+ </xsd:simpleType>
+
<xsd:simpleType name="profile">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="primary_profile_only"/>
@@ -110,6 +141,13 @@
</xsd:restriction>
</xsd:simpleType>
+ <xsd:simpleType name="initialDisplayStateOrStringResourceName">
+ <!-- String resource names will be resolved only once at parse time. -->
+ <!-- Locale changes and device config changes will be ignored. -->
+ <!-- The value of the string resource must be of type initialDisplayState. -->
+ <xsd:union memberTypes="stringResourceName initialDisplayState" />
+ </xsd:simpleType>
+
<xsd:simpleType name="initialDisplayState">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="enabled"/>
@@ -118,8 +156,13 @@
</xsd:restriction>
</xsd:simpleType>
- <!-- NOTE: stringResourceNames will be ignored for any attribute not explicitly marked as stringResourceName in this schema. -->
- <!-- A stringResourceNames is a fully qualified resource name of the form "@package:string/entry". Package is required. -->
+ <xsd:simpleType name="runtimeStringResourceName">
+ <!-- String resource names will be resolved at runtime whenever the string value is used. -->
+ <xsd:union memberTypes="stringResourceName" />
+ </xsd:simpleType>
+
+ <!-- String resource names will be ignored for any attribute not directly or indirectly marked as stringResourceName. -->
+ <!-- A stringResourceName is a fully qualified resource name of the form "@package:string/entry". Package is required. -->
<xsd:simpleType name="stringResourceName">
<xsd:restriction base="xsd:string">
<xsd:pattern value="@([a-z]+\.)*[a-z]+:string/.+"/>
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt
index 9f33c78ae..3c9ebe9e0 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt
@@ -114,7 +114,7 @@ class SafetySourceTest {
assertThat(DYNAMIC_ALL_OPTIONAL.summaryResId).isEqualTo(REFERENCE_RES_ID)
assertThat(DYNAMIC_DISABLED.summaryResId).isEqualTo(REFERENCE_RES_ID)
assertThat(DYNAMIC_HIDDEN.summaryResId).isEqualTo(Resources.ID_NULL)
- assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.summaryResId).isEqualTo(Resources.ID_NULL)
+ assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.summaryResId).isEqualTo(REFERENCE_RES_ID)
assertThat(STATIC_BAREBONE.summaryResId).isEqualTo(Resources.ID_NULL)
assertThat(STATIC_ALL_OPTIONAL.summaryResId).isEqualTo(REFERENCE_RES_ID)
assertThrows(UnsupportedOperationException::class.java) { ISSUE_ONLY_BAREBONE.summaryResId }
@@ -128,7 +128,7 @@ class SafetySourceTest {
assertThat(DYNAMIC_ALL_OPTIONAL.intentAction).isEqualTo(INTENT_ACTION)
assertThat(DYNAMIC_DISABLED.intentAction).isNull()
assertThat(DYNAMIC_HIDDEN.intentAction).isNull()
- assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.intentAction).isNull()
+ assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.intentAction).isEqualTo(INTENT_ACTION)
assertThat(STATIC_BAREBONE.intentAction).isEqualTo(INTENT_ACTION)
assertThat(STATIC_ALL_OPTIONAL.intentAction).isEqualTo(INTENT_ACTION)
assertThrows(UnsupportedOperationException::class.java) { ISSUE_ONLY_BAREBONE.intentAction }
@@ -542,6 +542,8 @@ class SafetySourceTest {
.setPackageName(PACKAGE_NAME)
.setTitleResId(REFERENCE_RES_ID)
.setTitleForWorkResId(REFERENCE_RES_ID)
+ .setSummaryResId(REFERENCE_RES_ID)
+ .setIntentAction(INTENT_ACTION)
.setProfile(SafetySource.PROFILE_ALL)
.setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN)
.setSearchTermsResId(REFERENCE_RES_ID)