diff options
27 files changed, 983 insertions, 94 deletions
diff --git a/SafetyCenter/Config/Android.bp b/SafetyCenter/Config/Android.bp index d6423288a..48c8eab46 100644 --- a/SafetyCenter/Config/Android.bp +++ b/SafetyCenter/Config/Android.bp @@ -40,6 +40,7 @@ java_library { ], static_libs: [ "modules-utils-build", + "permissions-aconfig-flags-lib", ], apex_available: [ "com.android.permission", diff --git a/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java b/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java index b6730f36f..38eee9e51 100644 --- a/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java +++ b/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java @@ -33,10 +33,12 @@ import android.content.res.Resources; import android.safetycenter.config.SafetyCenterConfig; import android.safetycenter.config.SafetySource; import android.safetycenter.config.SafetySourcesGroup; +import android.util.Log; import androidx.annotation.RequiresApi; import com.android.modules.utils.build.SdkLevel; +import com.android.permission.flags.Flags; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -49,6 +51,7 @@ import java.io.InputStream; @RequiresApi(TIRAMISU) public final class SafetyCenterConfigParser { + private static final String TAG = "SafetyCenterConfigParser"; private static final String TAG_SAFETY_CENTER_CONFIG = "safety-center-config"; private static final String TAG_SAFETY_SOURCES_CONFIG = "safety-sources-config"; private static final String TAG_SAFETY_SOURCES_GROUP = "safety-sources-group"; @@ -64,6 +67,8 @@ public final class SafetyCenterConfigParser { private static final String ATTR_SAFETY_SOURCE_PACKAGE_NAME = "packageName"; private static final String ATTR_SAFETY_SOURCE_TITLE = "title"; private static final String ATTR_SAFETY_SOURCE_TITLE_FOR_WORK = "titleForWork"; + private static final String ATTR_SAFETY_SOURCE_TITLE_FOR_PRIVATE_PROFILE = + "titleForPrivateProfile"; private static final String ATTR_SAFETY_SOURCE_SUMMARY = "summary"; private static final String ATTR_SAFETY_SOURCE_INTENT_ACTION = "intentAction"; private static final String ATTR_SAFETY_SOURCE_PROFILE = "profile"; @@ -270,6 +275,26 @@ public final class SafetyCenterConfigParser { parser.getAttributeName(i), resources)); break; + case ATTR_SAFETY_SOURCE_TITLE_FOR_PRIVATE_PROFILE: + if (SdkLevel.isAtLeastV()) { + if (Flags.privateProfileTitleApi()) { + builder.setTitleForPrivateProfileResId( + parseStringResourceName( + parser.getAttributeValue(i), + name, + parser.getAttributeName(i), + resources)); + } else { + Log.i( + TAG, + String.format( + "Ignoring attribute %s.%s", + name, ATTR_SAFETY_SOURCE_TITLE_FOR_PRIVATE_PROFILE)); + } + break; + } else { + throw attributeUnexpected(name, parser.getAttributeName(i)); + } case ATTR_SAFETY_SOURCE_SUMMARY: builder.setSummaryResId( parseStringResourceName( diff --git a/SafetyCenter/Config/tests/Android.bp b/SafetyCenter/Config/tests/Android.bp index 3adedfc25..20a536237 100644 --- a/SafetyCenter/Config/tests/Android.bp +++ b/SafetyCenter/Config/tests/Android.bp @@ -33,6 +33,7 @@ android_test { "compatibility-device-util-axt", "safety-center-config", "safety-center-test-util-lib", + "permissions-aconfig-flags-lib", ], test_suites: [ "general-tests", 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 64775d7fe..98b899ebf 100644 --- a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt +++ b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt @@ -19,6 +19,7 @@ package com.android.safetycenter.config import android.content.Context import androidx.test.core.app.ApplicationProvider.getApplicationContext import com.android.modules.utils.build.SdkLevel +import com.android.permission.flags.Flags import com.android.safetycenter.config.tests.R import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertThrows @@ -63,18 +64,39 @@ class ParserConfigInvalidTest { fun parameters() = arrayOf( Params( + "ConfigDynamicSafetySourceAllDisabledNoPrivate", + R.raw.config_dynamic_safety_source_all_disabled_no_private, + "Element dynamic-safety-source invalid", + "Required attribute titleForPrivateProfile missing", + SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi() + ), + Params( "ConfigDynamicSafetySourceAllDisabledNoWork", R.raw.config_dynamic_safety_source_all_disabled_no_work, "Element dynamic-safety-source invalid", "Required attribute titleForWork missing" ), Params( + "ConfigDynamicSafetySourceAllHiddenWithSearchNoPrivate", + R.raw.config_dynamic_safety_source_all_hidden_with_search_no_private, + "Element dynamic-safety-source invalid", + "Required attribute titleForPrivateProfile missing", + SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi() + ), + Params( "ConfigDynamicSafetySourceAllHiddenWithSearchNoWork", R.raw.config_dynamic_safety_source_all_hidden_with_search_no_work, "Element dynamic-safety-source invalid", "Required attribute titleForWork missing" ), Params( + "ConfigDynamicSafetySourceAllNoPrivate", + R.raw.config_dynamic_safety_source_all_no_private, + "Element dynamic-safety-source invalid", + "Required attribute titleForPrivateProfile missing", + SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi() + ), + Params( "ConfigDynamicSafetySourceAllNoWork", R.raw.config_dynamic_safety_source_all_no_work, "Element dynamic-safety-source invalid", @@ -160,12 +182,26 @@ class ParserConfigInvalidTest { "Required attribute title missing" ), Params( + "ConfigDynamicSafetySourcePrimaryHiddenWithPrivate", + R.raw.config_dynamic_safety_source_primary_hidden_with_private, + "Element dynamic-safety-source invalid", + "Prohibited attribute titleForPrivateProfile present", + SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi() + ), + Params( "ConfigDynamicSafetySourcePrimaryHiddenWithWork", R.raw.config_dynamic_safety_source_primary_hidden_with_work, "Element dynamic-safety-source invalid", "Prohibited attribute titleForWork present" ), Params( + "ConfigDynamicSafetySourcePrimaryWithPrivate", + R.raw.config_dynamic_safety_source_primary_with_private, + "Element dynamic-safety-source invalid", + "Prohibited attribute titleForPrivateProfile present", + SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi() + ), + Params( "ConfigDynamicSafetySourcePrimaryWithWork", R.raw.config_dynamic_safety_source_primary_with_work, "Element dynamic-safety-source invalid", @@ -226,6 +262,13 @@ class ParserConfigInvalidTest { "Prohibited attribute intentAction present" ), Params( + "ConfigIssueOnlySafetySourceWithPrivate", + R.raw.config_issue_only_safety_source_with_private, + "Element issue-only-safety-source invalid", + "Prohibited attribute titleForPrivateProfile present", + SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi() + ), + Params( "ConfigIssueOnlySafetySourceWithSearch", R.raw.config_issue_only_safety_source_with_search, "Element issue-only-safety-source invalid", @@ -425,12 +468,26 @@ class ParserConfigInvalidTest { SdkLevel.isAtLeastU() ), Params( + "ConfigStaticSafetySourceWithPrimaryAndPrivate", + R.raw.config_static_safety_source_with_primary_and_private, + "Element static-safety-source invalid", + "Prohibited attribute titleForPrivateProfile present", + SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi() + ), + Params( "ConfigStaticSafetySourceWithPrimaryAndWork", R.raw.config_static_safety_source_with_primary_and_work, "Element static-safety-source invalid", "Prohibited attribute titleForWork present" ), Params( + "ConfigStaticSafetySourceWithPrivatePreV", + R.raw.config_static_safety_source_with_private_profile, + "Element static-safety-source invalid", + "Unexpected attribute static-safety-source.titleForPrivateProfile", + !SdkLevel.isAtLeastV() + ), + Params( "ConfigStaticSafetySourceWithRefresh", R.raw.config_static_safety_source_with_refresh, "Element static-safety-source invalid", 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 b63ccead7..5e8d04597 100644 --- a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt +++ b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt @@ -77,6 +77,9 @@ class ParserConfigValidTest { addPackageCertificateHash("feed1") addPackageCertificateHash("feed2") } + if (SdkLevel.isAtLeastV()) { + setTitleForPrivateProfileResId(R.string.reference) + } } .build() ) @@ -101,6 +104,9 @@ class ParserConfigValidTest { addPackageCertificateHash("feed1") addPackageCertificateHash("feed2") } + if (SdkLevel.isAtLeastV()) { + setTitleForPrivateProfileResId(R.string.reference) + } } .build() ) @@ -133,6 +139,11 @@ class ParserConfigValidTest { .setProfile(SafetySource.PROFILE_ALL) .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN) .setSearchTermsResId(R.string.reference) + .apply { + if (SdkLevel.isAtLeastV()) { + setTitleForPrivateProfileResId(R.string.reference) + } + } .build() ) .build() @@ -159,6 +170,11 @@ class ParserConfigValidTest { .setIntentAction("intent") .setProfile(SafetySource.PROFILE_ALL) .setSearchTermsResId(R.string.reference) + .apply { + if (SdkLevel.isAtLeastV()) { + setTitleForPrivateProfileResId(R.string.reference) + } + } .build() ) .build() diff --git a/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_disabled_no_private.xml b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_disabled_no_private.xml new file mode 100644 index 000000000..dae6ca754 --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_disabled_no_private.xml @@ -0,0 +1,18 @@ +<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" + titleForWork="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="all_profiles" + initialDisplayState="disabled"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_hidden_with_search_no_private.xml b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_hidden_with_search_no_private.xml new file mode 100644 index 000000000..e1852b6ec --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_hidden_with_search_no_private.xml @@ -0,0 +1,17 @@ +<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" + titleForWork="@com.android.safetycenter.config.tests:string/reference" + profile="all_profiles" + initialDisplayState="hidden" + searchTerms="@com.android.safetycenter.config.tests:string/reference"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_no_private.xml b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_no_private.xml new file mode 100644 index 000000000..8446b71cc --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_no_private.xml @@ -0,0 +1,17 @@ +<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" + titleForWork="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="all_profiles"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_primary_hidden_with_private.xml b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_primary_hidden_with_private.xml new file mode 100644 index 000000000..3d5840b03 --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_primary_hidden_with_private.xml @@ -0,0 +1,15 @@ +<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" + titleForPrivateProfile="@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-v35/config_dynamic_safety_source_primary_with_private.xml b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_primary_with_private.xml new file mode 100644 index 000000000..b95a3ed6e --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_primary_with_private.xml @@ -0,0 +1,17 @@ +<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" + titleForPrivateProfile="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw-v35/config_issue_only_safety_source_with_private.xml b/SafetyCenter/Config/tests/res/raw-v35/config_issue_only_safety_source_with_private.xml new file mode 100644 index 000000000..7b2484a41 --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v35/config_issue_only_safety_source_with_private.xml @@ -0,0 +1,12 @@ +<safety-center-config> + <safety-sources-config> + <safety-sources-group + id="id"> + <issue-only-safety-source + id="id" + packageName="package" + profile="all_profiles" + titleForPrivateProfile="@com.android.safetycenter.config.tests:string/reference"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw-v35/config_static_safety_source_with_primary_and_private.xml b/SafetyCenter/Config/tests/res/raw-v35/config_static_safety_source_with_primary_and_private.xml new file mode 100644 index 000000000..0ab3d885b --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v35/config_static_safety_source_with_primary_and_private.xml @@ -0,0 +1,16 @@ +<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"> + <static-safety-source + id="id" + title="@com.android.safetycenter.config.tests:string/reference" + titleForPrivateProfile="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw-v35/config_valid.xml b/SafetyCenter/Config/tests/res/raw-v35/config_valid.xml new file mode 100644 index 000000000..e49a6cdc4 --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v35/config_valid.xml @@ -0,0 +1,197 @@ +<safety-center-config> + <safety-sources-config> + <safety-sources-group + id="dynamic" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + statelessIconType="privacy"> + <dynamic-safety-source + id="dynamic_barebone" + packageName="package" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + <dynamic-safety-source + id="dynamic_all_optional" + packageName="package" + title="@com.android.safetycenter.config.tests:string/reference" + titleForWork="@com.android.safetycenter.config.tests:string/reference" + titleForPrivateProfile="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="all_profiles" + initialDisplayState="disabled" + maxSeverityLevel="300" + searchTerms="@com.android.safetycenter.config.tests:string/reference" + loggingAllowed="false" + refreshOnPageOpenAllowed="true" + notificationsAllowed="true" + deduplicationGroup="group" + packageCertificateHashes="feed1,feed2"/> + <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" + titleForPrivateProfile="@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" + notificationsAllowed="@com.android.safetycenter.config.tests:string/dynamic_all_references_notifications_allowed" + deduplicationGroup="@com.android.safetycenter.config.tests:string/dynamic_all_references_deduplication_group" + packageCertificateHashes="@com.android.safetycenter.config.tests:string/dynamic_all_references_package_cert_hashes"/> + <dynamic-safety-source + id="dynamic_disabled" + packageName="package" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + profile="primary_profile_only" + initialDisplayState="disabled"/> + <dynamic-safety-source + id="dynamic_hidden" + packageName="package" + profile="all_profiles" + initialDisplayState="hidden"/> + <dynamic-safety-source + id="dynamic_hidden_with_search" + packageName="package" + title="@com.android.safetycenter.config.tests:string/reference" + titleForWork="@com.android.safetycenter.config.tests:string/reference" + titleForPrivateProfile="@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"/> + </safety-sources-group> + <safety-sources-group + id="static" + title="@com.android.safetycenter.config.tests:string/reference"> + <static-safety-source + id="static_barebone" + title="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + <static-safety-source + id="static_all_optional" + packageName="package" + title="@com.android.safetycenter.config.tests:string/reference" + titleForWork="@com.android.safetycenter.config.tests:string/reference" + titleForPrivateProfile="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="all_profiles" + searchTerms="@com.android.safetycenter.config.tests:string/reference"/> + </safety-sources-group> + <safety-sources-group + id="issue_only"> + <issue-only-safety-source + id="issue_only_barebone" + packageName="package" + profile="primary_profile_only"/> + <issue-only-safety-source + id="issue_only_all_optional" + packageName="package" + profile="all_profiles" + maxSeverityLevel="300" + loggingAllowed="false" + refreshOnPageOpenAllowed="true" + notificationsAllowed="true" + deduplicationGroup="group" + packageCertificateHashes="feed1,feed2"/> + <issue-only-safety-source + id="id_test_abcxyz_ABCXYZ_012789" + packageName="package" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + id="mixed" + title="@com.android.safetycenter.config.tests:string/reference"> + <dynamic-safety-source + id="mixed_dynamic_barebone" + packageName="package" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + <issue-only-safety-source + id="mixed_issue_only_barebone" + packageName="package" + profile="primary_profile_only"/> + <static-safety-source + id="mixed_static_barebone" + title="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="stateful" + id="stateful_barebone" + title="@com.android.safetycenter.config.tests:string/reference"> + <static-safety-source + id="stateful_barebone_source" + title="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="stateful" + id="stateful_all_optional" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + statelessIconType="privacy"> + <static-safety-source + id="stateful_all_optional_source" + title="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="stateless" + id="stateless_barebone" + title="@com.android.safetycenter.config.tests:string/reference"> + <static-safety-source + id="stateless_barebone_source" + title="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="stateless" + id="stateless_all_optional" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + statelessIconType="privacy"> + <static-safety-source + id="stateless_all_optional_source" + title="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="hidden" + id="hidden_barebone"> + <issue-only-safety-source + id="hidden_barebone_source" + packageName="package" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="hidden" + id="hidden_all_optional" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + statelessIconType="privacy"> + <issue-only-safety-source + id="hidden_all_optional_source" + packageName="package" + profile="primary_profile_only"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw/config_static_safety_source_with_private_profile.xml b/SafetyCenter/Config/tests/res/raw/config_static_safety_source_with_private_profile.xml new file mode 100644 index 000000000..f790baec9 --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw/config_static_safety_source_with_private_profile.xml @@ -0,0 +1,17 @@ +<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"> + <static-safety-source + id="id" + title="@com.android.safetycenter.config.tests:string/reference" + titleForWork="@com.android.safetycenter.config.tests:string/reference" + titleForPrivateProfile="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="all_profiles"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/ConfigLintChecker/Android.bp b/SafetyCenter/ConfigLintChecker/Android.bp index fad0165df..7c615d2f4 100644 --- a/SafetyCenter/ConfigLintChecker/Android.bp +++ b/SafetyCenter/ConfigLintChecker/Android.bp @@ -12,64 +12,65 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - default_team: "trendy_team_android_permissions", - default_applicable_licenses: ["Android-Apache-2.0"], -} +// TODO(b/322944911): Reconsider enabling linter checker +//package { +// default_team: "trendy_team_android_permissions", +// default_applicable_licenses: ["Android-Apache-2.0"], +//} -java_library_host { - name: "ConfigLintChecker", - srcs: [ - "java/**/*.java", - "java/**/*.kt", - ":safetycenter-annotations-sources", - ":safetycenter-config-api-sources", - ":safetycenter-config-parser-sources", - ], - plugins: ["auto_service_plugin"], - libs: [ - "androidx.annotation_annotation", // For androidx.annotation.RequiresApi - "auto_service_annotations", - "core-xml-for-host", // For org.xmlpull.v1.* - "framework-annotations-lib", // For com.android.annotation.* - "layoutlib_api-prebuilt", // For com.android.resources.ResourceFolderType - "lint_api", - ], - java_resources: [":safetycenter-config-schemas"], - jarjar_rules: "jarjar-rules.txt", - kotlincflags: ["-Xjvm-default=all"], - visibility: [ - "//packages/modules/Permission:__subpackages__", - "//vendor:__subpackages__", - ], -} +//java_library_host { +// name: "ConfigLintChecker", +// srcs: [ +// "java/**/*.java", +// "java/**/*.kt", +// ":safetycenter-annotations-sources", +// ":safetycenter-config-api-sources", +// ":safetycenter-config-parser-sources", +// ], +// plugins: ["auto_service_plugin"], +// libs: [ +// "androidx.annotation_annotation", // For androidx.annotation.RequiresApi +// "auto_service_annotations", +// "core-xml-for-host", // For org.xmlpull.v1.* +// "framework-annotations-lib", // For com.android.annotation.* +// "layoutlib_api-prebuilt", // For com.android.resources.ResourceFolderType +// "lint_api", +// ], +// java_resources: [":safetycenter-config-schemas"], +// jarjar_rules: "jarjar-rules.txt", +// kotlincflags: ["-Xjvm-default=all"], +// visibility: [ +// "//packages/modules/Permission:__subpackages__", +// "//vendor:__subpackages__", +// ], +//} -java_test_host { - name: "ConfigLintCheckerTest", - srcs: [ - "tests/java/**/*.kt", - ], - static_libs: [ - "ConfigLintChecker", - "junit", - "lint", - "lint_tests", - ], - test_options: { - unit_test: true, - tradefed_options: [ - { - // lint bundles in some classes that were built with older versions - // of libraries, and no longer load. Since tradefed tries to load - // all classes in the jar to look for tests, it crashes loading them. - // Exclude these classes from tradefed's search. - name: "exclude-paths", - value: "org/apache", - }, - { - name: "exclude-paths", - value: "META-INF", - }, - ], - }, -} +//java_test_host { +// name: "ConfigLintCheckerTest", +// srcs: [ +// "tests/java/**/*.kt", +// ], +// static_libs: [ +// "ConfigLintChecker", +// "junit", +// "lint", +// "lint_tests", +// ], +// test_options: { +// unit_test: true, +// tradefed_options: [ +// { +// // lint bundles in some classes that were built with older versions +// // of libraries, and no longer load. Since tradefed tries to load +// // all classes in the jar to look for tests, it crashes loading them. +// // Exclude these classes from tradefed's search. +// name: "exclude-paths", +// value: "org/apache", +// }, +// { +// name: "exclude-paths", +// value: "META-INF", +// }, +// ], +// }, +//} diff --git a/SafetyCenter/Resources/Android.bp b/SafetyCenter/Resources/Android.bp index 6f635c885..a10ea7f1a 100644 --- a/SafetyCenter/Resources/Android.bp +++ b/SafetyCenter/Resources/Android.bp @@ -42,9 +42,10 @@ android_app { min_sdk_version: "30", apex_available: ["com.android.permission"], certificate: ":com.android.safetycenter.resources.certificate", - lint: { - extra_check_modules: ["ConfigLintChecker"], - }, + // TODO(b/322944911): Reconsider enabling linter checker + //lint: { + // extra_check_modules: ["ConfigLintChecker"], + //}, static_libs: [ "SafetyCenterResourcesShared", ], diff --git a/SafetyCenter/Resources/res/raw-v35/safety_center_config.xml b/SafetyCenter/Resources/res/raw-v35/safety_center_config.xml index 5d626757c..cb49e9d36 100644 --- a/SafetyCenter/Resources/res/raw-v35/safety_center_config.xml +++ b/SafetyCenter/Resources/res/raw-v35/safety_center_config.xml @@ -35,6 +35,7 @@ profile="all_profiles" title="@com.android.safetycenter.resources:string/biometrics_title" titleForWork="@com.android.safetycenter.resources:string/biometrics_title_for_work" + titleForPrivateProfile="@com.android.safetycenter.resources:string/biometrics_title_for_private_profile" searchTerms="@com.android.safetycenter.resources:string/biometrics_search_terms" initialDisplayState="hidden"/> </safety-sources-group> diff --git a/SafetyCenter/Resources/res/values-v35/strings.xml b/SafetyCenter/Resources/res/values-v35/strings.xml index af43f11f2..bdbc4b648 100644 --- a/SafetyCenter/Resources/res/values-v35/strings.xml +++ b/SafetyCenter/Resources/res/values-v35/strings.xml @@ -20,6 +20,9 @@ <string name="cellular_network_security_title" description="The title of the group of safety settings relating to cellular network security">Cellular network security</string> <string name="cellular_network_security_summary" description="The summary of the group of safety settings relating to cellular network security, which describes the group contents">Network type, encryption, notification controls</string> + <!-- Device unlock --> + <string name="biometrics_title_for_private_profile" description="The default title of the setting for managing biometric options on the device for private space"><!-- Empty placeholder--></string> + <!-- More settings --> <string name="private_space_title" description="The title of the entry for Private Space">Private Space</string> <string name="private_space_summary" description="The summary of the entry for Private Space settings, which describes the page contents">Setup Private Space, and more</string> diff --git a/flags/flags.aconfig b/flags/flags.aconfig index 441e3584e..8bf3a63ab 100644 --- a/flags/flags.aconfig +++ b/flags/flags.aconfig @@ -15,6 +15,14 @@ flag { is_fixed_read_only: true } +flag { + name: "private_profile_title_api" + namespace: "permissions" + description: "This flag is used to guard the private profile title api in safety center" + bug: "286539356" + is_fixed_read_only: true +} + # TODO: Remove when wear_privacy_dashboard_enabled_read_only reaches to the next stage. flag { name: "wear_privacy_dashboard_enabled" diff --git a/framework-s/api/system-current.txt b/framework-s/api/system-current.txt index b4dfafba2..3712eae97 100644 --- a/framework-s/api/system-current.txt +++ b/framework-s/api/system-current.txt @@ -570,6 +570,7 @@ package android.safetycenter.config { method public int getProfile(); method @StringRes public int getSearchTermsResId(); method @StringRes public int getSummaryResId(); + method @FlaggedApi("com.android.permission.flags.private_profile_title_api") @StringRes public int getTitleForPrivateProfileResId(); method @StringRes public int getTitleForWorkResId(); method @StringRes public int getTitleResId(); method public int getType(); @@ -605,6 +606,7 @@ package android.safetycenter.config { method @NonNull public android.safetycenter.config.SafetySource.Builder setRefreshOnPageOpenAllowed(boolean); method @NonNull public android.safetycenter.config.SafetySource.Builder setSearchTermsResId(@StringRes int); method @NonNull public android.safetycenter.config.SafetySource.Builder setSummaryResId(@StringRes int); + method @FlaggedApi("com.android.permission.flags.private_profile_title_api") @NonNull public android.safetycenter.config.SafetySource.Builder setTitleForPrivateProfileResId(@StringRes int); method @NonNull public android.safetycenter.config.SafetySource.Builder setTitleForWorkResId(@StringRes int); method @NonNull public android.safetycenter.config.SafetySource.Builder setTitleResId(@StringRes int); } diff --git a/framework-s/java/android/safetycenter/config/SafetySource.java b/framework-s/java/android/safetycenter/config/SafetySource.java index 8aa897850..5502ca950 100644 --- a/framework-s/java/android/safetycenter/config/SafetySource.java +++ b/framework-s/java/android/safetycenter/config/SafetySource.java @@ -18,9 +18,11 @@ package android.safetycenter.config; import static android.os.Build.VERSION_CODES.TIRAMISU; import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; +import static android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM; import static java.util.Objects.requireNonNull; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -34,6 +36,7 @@ import android.util.ArraySet; import androidx.annotation.RequiresApi; import com.android.modules.utils.build.SdkLevel; +import com.android.permission.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -182,6 +185,9 @@ public final class SafetySource implements Parcelable { builder.addPackageCertificateHash(certs.get(i)); } } + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + builder.setTitleForPrivateProfileResId(in.readInt()); + } return builder.build(); } @@ -207,6 +213,7 @@ public final class SafetySource implements Parcelable { private final boolean mNotificationsAllowed; @Nullable final String mDeduplicationGroup; @NonNull private final Set<String> mPackageCertificateHashes; + @StringRes private final int mTitleForPrivateProfileResId; private SafetySource( @SafetySourceType int type, @@ -224,7 +231,8 @@ public final class SafetySource implements Parcelable { boolean refreshOnPageOpenAllowed, boolean notificationsAllowed, @Nullable String deduplicationGroup, - @NonNull Set<String> packageCertificateHashes) { + @NonNull Set<String> packageCertificateHashes, + @StringRes int titleForPrivateProfileResId) { mType = type; mId = id; mPackageName = packageName; @@ -241,6 +249,7 @@ public final class SafetySource implements Parcelable { mNotificationsAllowed = notificationsAllowed; mDeduplicationGroup = deduplicationGroup; mPackageCertificateHashes = Set.copyOf(packageCertificateHashes); + mTitleForPrivateProfileResId = titleForPrivateProfileResId; } /** Returns the type of this safety source. */ @@ -347,6 +356,37 @@ public final class SafetySource implements Parcelable { } /** + * Returns the resource id of the title for private profile of this safety source. + * + * <p>The id refers to a string resource that is either accessible from any resource context or + * that is accessible from the same resource context that was used to load the Safety Center + * configuration. The id is {@link Resources#ID_NULL} when a title for private profile is not + * provided. + * + * @throws UnsupportedOperationException if the source is of type {@link + * SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY} or if the profile property of the source is + * set to {@link SafetySource#PROFILE_PRIMARY} + */ + @FlaggedApi(Flags.FLAG_PRIVATE_PROFILE_TITLE_API) + @RequiresApi(VANILLA_ICE_CREAM) + @StringRes + public int getTitleForPrivateProfileResId() { + if (!SdkLevel.isAtLeastV()) { + throw new UnsupportedOperationException( + "getTitleForPrivateProfileResId unsupported for SDKs lower than V"); + } + if (mType == SAFETY_SOURCE_TYPE_ISSUE_ONLY) { + throw new UnsupportedOperationException( + "getTitleForPrivateProfileResId unsupported for issue-only safety source"); + } + if (mProfile == PROFILE_PRIMARY) { + throw new UnsupportedOperationException( + "getTitleForPrivateProfileResId unsupported for primary profile safety source"); + } + return mTitleForPrivateProfileResId; + } + + /** * Returns the resource id of the summary of this safety source. * * <p>The id refers to a string resource that is either accessible from any resource context or @@ -554,7 +594,8 @@ public final class SafetySource implements Parcelable { && mRefreshOnPageOpenAllowed == that.mRefreshOnPageOpenAllowed && mNotificationsAllowed == that.mNotificationsAllowed && Objects.equals(mDeduplicationGroup, that.mDeduplicationGroup) - && Objects.equals(mPackageCertificateHashes, that.mPackageCertificateHashes); + && Objects.equals(mPackageCertificateHashes, that.mPackageCertificateHashes) + && mTitleForPrivateProfileResId == that.mTitleForPrivateProfileResId; } @Override @@ -575,7 +616,8 @@ public final class SafetySource implements Parcelable { mRefreshOnPageOpenAllowed, mNotificationsAllowed, mDeduplicationGroup, - mPackageCertificateHashes); + mPackageCertificateHashes, + mTitleForPrivateProfileResId); } @Override @@ -613,6 +655,8 @@ public final class SafetySource implements Parcelable { + mDeduplicationGroup + ", mPackageCertificateHashes=" + mPackageCertificateHashes + + ", mTitleForPrivateProfileResId=" + + mTitleForPrivateProfileResId + '}'; } @@ -641,6 +685,9 @@ public final class SafetySource implements Parcelable { dest.writeString(mDeduplicationGroup); dest.writeStringList(List.copyOf(mPackageCertificateHashes)); } + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + dest.writeInt(mTitleForPrivateProfileResId); + } } /** Builder class for {@link SafetySource}. */ @@ -662,6 +709,7 @@ public final class SafetySource implements Parcelable { @Nullable private Boolean mNotificationsAllowed; @Nullable private String mDeduplicationGroup; @NonNull private final ArraySet<String> mPackageCertificateHashes = new ArraySet<>(); + @Nullable @StringRes private Integer mTitleForPrivateProfileResId; /** Creates a {@link Builder} for a {@link SafetySource}. */ public Builder(@SafetySourceType int type) { @@ -692,6 +740,7 @@ public final class SafetySource implements Parcelable { mNotificationsAllowed = safetySource.mNotificationsAllowed; mDeduplicationGroup = safetySource.mDeduplicationGroup; mPackageCertificateHashes.addAll(safetySource.mPackageCertificateHashes); + mTitleForPrivateProfileResId = safetySource.mTitleForPrivateProfileResId; } /** @@ -759,6 +808,32 @@ public final class SafetySource implements Parcelable { } /** + * Sets the resource id of the title for work of this safety source. + * + * <p>The id must refer to a string resource that is either accessible from any resource + * context or that is accessible from the same resource context that was used to load the + * Safety Center configuration. The id defaults to {@link Resources#ID_NULL} when a title + * for work is not provided. + * + * <p>The title for work is required if the profile property of the source is set to {@link + * SafetySource#PROFILE_ALL} and either the source is of type static or the source is a + * source of type dynamic that is not hidden and that does not provide search terms. The + * title for work is prohibited for sources of type issue-only and if the profile property + * of the source is not set to {@link SafetySource#PROFILE_ALL}. + */ + @FlaggedApi(Flags.FLAG_PRIVATE_PROFILE_TITLE_API) + @RequiresApi(VANILLA_ICE_CREAM) + @NonNull + public Builder setTitleForPrivateProfileResId(@StringRes int titleForPrivateProfileResId) { + if (!SdkLevel.isAtLeastV()) { + throw new UnsupportedOperationException( + "setTitleForPrivateProfileResId unsupported for SDKs lower than V"); + } + mTitleForPrivateProfileResId = titleForPrivateProfileResId; + return this; + } + + /** * Sets the resource id of the summary of this safety source. * * <p>The id must refer to a string resource that is either accessible from any resource @@ -984,7 +1059,7 @@ public final class SafetySource implements Parcelable { PROFILE_NONE, PROFILE_PRIMARY, PROFILE_ALL); - boolean hasWork = profile == PROFILE_ALL; + boolean hasAllProfiles = profile == PROFILE_ALL; int searchTermsResId = BuilderUtils.validateResId( @@ -1000,8 +1075,8 @@ public final class SafetySource implements Parcelable { BuilderUtils.validateResId( mTitleForWorkResId, "titleForWork", - hasWork && titleRequired, - !hasWork || isIssueOnly); + hasAllProfiles && titleRequired, + !hasAllProfiles || isIssueOnly); int summaryResId = BuilderUtils.validateResId( @@ -1052,6 +1127,16 @@ public final class SafetySource implements Parcelable { packageCertificateHashes, "packageCertificateHashes", false, isStatic); } + int titleForPrivateProfileResId = Resources.ID_NULL; + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + titleForPrivateProfileResId = + BuilderUtils.validateResId( + mTitleForPrivateProfileResId, + "titleForPrivateProfile", + hasAllProfiles && titleRequired, + !hasAllProfiles || isIssueOnly); + } + return new SafetySource( type, id, @@ -1068,7 +1153,8 @@ public final class SafetySource implements Parcelable { refreshOnPageOpenAllowed, notificationsAllowed, deduplicationGroup, - packageCertificateHashes); + packageCertificateHashes, + titleForPrivateProfileResId); } } } diff --git a/framework-s/java/android/safetycenter/config/safety_center_config-v35.xsd b/framework-s/java/android/safetycenter/config/safety_center_config-v35.xsd new file mode 100644 index 000000000..20b1e7655 --- /dev/null +++ b/framework-s/java/android/safetycenter/config/safety_center_config-v35.xsd @@ -0,0 +1,223 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<!-- This file contains comments that define constraints that cannot be covered by the XSD language --> +<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" + version="1.0"> + + <xsd:element name="safety-center-config" type="safety-center-config"/> + + <xsd:complexType name="safety-center-config"> + <xsd:sequence> + <xsd:element name="safety-sources-config" type="safety-sources-config"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="safety-sources-config"> + <xsd:sequence> + <xsd:element + name="safety-sources-group" type="safety-sources-group" + minOccurs="1" maxOccurs="unbounded"/> + </xsd:sequence> + </xsd:complexType> + + <xsd:complexType name="safety-sources-group"> + <xsd:choice minOccurs="1" maxOccurs="unbounded"> + <xsd:element name="dynamic-safety-source" type="dynamic-safety-source"/> + <xsd:element name="static-safety-source" type="static-safety-source"/> + <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="idOrStringResourceName" use="required"/> + <!-- title is required unless the group contains issue only and/or internal sources --> + <xsd:attribute name="title" type="runtimeStringResourceName"/> + <xsd:attribute name="summary" type="runtimeStringResourceName"/> + <xsd:attribute name="statelessIconType" type="statelessIconTypeOrStringResourceName" + default="none"/> + <!-- type is inferred from other attributes and the group content if omitted --> + <xsd:attribute name="type" type="groupTypeOrStringResourceName"/> + </xsd:complexType> + + <xsd:complexType name="dynamic-safety-source"> + <!-- id must be unique among safety sources --> + <xsd:attribute name="id" type="idOrStringResourceName" use="required"/> + <xsd:attribute name="packageName" type="stringOrStringResourceName" use="required"/> + <!-- optional comma-separated set of certificate hashes, if provided will be used for validation. --> + <xsd:attribute name="packageCertificateHashes" type="stringOrStringResourceName"/> + <!-- title is required if initialDisplayState is not set to hidden or if searchTerms are provided --> + <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 --> + <xsd:attribute name="titleForWork" type="runtimeStringResourceName"/> + <!-- titleForPrivateProfile is required if profile is set to all_profiles, and initialDisplayState is not set to hidden or if searchTerms are provided --> + <!-- titleForPrivateProfile is prohibited if profile is set to primary_profile_only --> + <xsd:attribute name="titleForPrivateProfile" type="runtimeStringResourceName"/> + <!-- summary is required if initialDisplayState is not set to hidden --> + <xsd:attribute name="summary" type="runtimeStringResourceName"/> + <!-- intentAction is required if initialDisplayState is set to enabled --> + <xsd:attribute name="intentAction" type="stringOrStringResourceName"/> + <xsd:attribute name="profile" type="profile" use="required"/> + <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:attribute name="notificationsAllowed" type="booleanOrStringResourceName" + default="false"/> + <xsd:attribute name="deduplicationGroup" type="stringOrStringResourceName"/> + </xsd:complexType> + + <xsd:complexType name="issue-only-safety-source"> + <!-- id must be unique among safety sources --> + <xsd:attribute name="id" type="idOrStringResourceName" use="required"/> + <xsd:attribute name="packageName" type="stringOrStringResourceName" use="required"/> + <!-- optional comma-separated set of certificate hashes, if provided will be used for validation. --> + <xsd:attribute name="packageCertificateHashes" type="stringOrStringResourceName"/> + <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:attribute name="notificationsAllowed" type="booleanOrStringResourceName" + default="false"/> + <xsd:attribute name="deduplicationGroup" type="stringOrStringResourceName"/> + </xsd:complexType> + + <xsd:complexType name="static-safety-source"> + <!-- id must be unique among safety sources --> + <xsd:attribute name="id" type="idOrStringResourceName" use="required"/> + <xsd:attribute name="packageName" type="stringOrStringResourceName"/> + <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="runtimeStringResourceName"/> + <!-- titleForPrivateProfile is required if profile is set to all_profiles --> + <!-- titleForPrivateProfile is prohibited if profile is set to primary_profile_only --> + <xsd:attribute name="titleForPrivateProfile" 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="idOrStringResourceName"> + <!-- 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 id"/> + </xsd:simpleType> + + <xsd:simpleType name="id"> + <xsd:restriction base="xsd:string"> + <xsd:pattern value="[0-9a-zA-Z_-]+"/> + </xsd:restriction> + </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"/> + <xsd:enumeration value="privacy"/> + </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"/> + <xsd:enumeration value="all_profiles"/> + </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"/> + <xsd:enumeration value="disabled"/> + <xsd:enumeration value="hidden"/> + </xsd:restriction> + </xsd:simpleType> + + <xsd:simpleType name="groupTypeOrStringResourceName"> + <!-- 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 groupType. --> + <xsd:union memberTypes="stringResourceName groupType"/> + </xsd:simpleType> + + <xsd:simpleType name="groupType"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="stateless"/> + <xsd:enumeration value="stateful"/> + <xsd:enumeration value="hidden"/> + </xsd:restriction> + </xsd:simpleType> + + <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/.+"/> + </xsd:restriction> + </xsd:simpleType> + +</xsd:schema> diff --git a/service/java/com/android/safetycenter/SafetyCenterDataFactory.java b/service/java/com/android/safetycenter/SafetyCenterDataFactory.java index d485af997..7c7ade3f1 100644 --- a/service/java/com/android/safetycenter/SafetyCenterDataFactory.java +++ b/service/java/com/android/safetycenter/SafetyCenterDataFactory.java @@ -17,6 +17,7 @@ package com.android.safetycenter; import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; +import static android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM; import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_MANAGED; import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_PRIMARY; @@ -56,6 +57,7 @@ import android.util.Log; import androidx.annotation.Nullable; import com.android.modules.utils.build.SdkLevel; +import com.android.permission.flags.Flags; import com.android.safetycenter.UserProfileGroup.ProfileType; import com.android.safetycenter.data.SafetyCenterDataManager; import com.android.safetycenter.internaldata.SafetyCenterBundles; @@ -1234,8 +1236,11 @@ public final class SafetyCenterDataFactory { safetySource.getId(), safetySource.getTitleForWorkResId()); case PROFILE_TYPE_PRIVATE: - //TODO(b/286539356) replace with the appropriate way to get the title when - // available + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + return mSafetyCenterResourcesApk.getString( + safetySource.getTitleForPrivateProfileResId()); + } + Log.w(TAG, "unsupported private profile type encountered"); return mSafetyCenterResourcesApk.getString(safetySource.getTitleResId()); default: Log.w(TAG, "unexpected value for the profile type " + profileType); 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 59cc6547a..c5c5c218d 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt @@ -18,11 +18,13 @@ package android.safetycenter.cts.config import android.content.res.Resources import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE +import android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM import android.safetycenter.config.SafetySource import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.truth.os.ParcelableSubject.assertThat import androidx.test.filters.SdkSuppress import com.android.modules.utils.build.SdkLevel +import com.android.permission.flags.Flags import com.android.safetycenter.testing.EqualsHashCodeToStringTester import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertThrows @@ -126,6 +128,31 @@ class SafetySourceTest { } } + @SdkSuppress(minSdkVersion = VANILLA_ICE_CREAM) + @Test + fun getTitleForPrivateProfileResId_returnsTitleForPrivateProfileResIdOrThrows() { + assertThrows(UnsupportedOperationException::class.java) { + DYNAMIC_BAREBONE.titleForPrivateProfileResId + } + assertThat(dynamicAllOptional().titleForPrivateProfileResId).isEqualTo(REFERENCE_RES_ID) + assertThrows(UnsupportedOperationException::class.java) { + DYNAMIC_DISABLED.titleForPrivateProfileResId + } + assertThat(DYNAMIC_HIDDEN.titleForPrivateProfileResId).isEqualTo(Resources.ID_NULL) + assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.titleForPrivateProfileResId) + .isEqualTo(REFERENCE_RES_ID) + assertThrows(UnsupportedOperationException::class.java) { + STATIC_BAREBONE.titleForPrivateProfileResId + } + assertThat(STATIC_ALL_OPTIONAL.titleForPrivateProfileResId).isEqualTo(REFERENCE_RES_ID) + assertThrows(UnsupportedOperationException::class.java) { + ISSUE_ONLY_BAREBONE.titleForPrivateProfileResId + } + assertThrows(UnsupportedOperationException::class.java) { + issueOnlyAllOptional().titleForPrivateProfileResId + } + } + @Test fun getSummaryResId_returnsSummaryResIdOrThrows() { assertThat(DYNAMIC_BAREBONE.summaryResId).isEqualTo(REFERENCE_RES_ID) @@ -360,6 +387,9 @@ class SafetySourceTest { setDeduplicationGroup(DEDUPLICATION_GROUP) addPackageCertificateHash(HASH1) } + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } } .build() ) @@ -390,6 +420,9 @@ class SafetySourceTest { setDeduplicationGroup(DEDUPLICATION_GROUP) addPackageCertificateHash(HASH1) } + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } } .build() ) @@ -413,6 +446,9 @@ class SafetySourceTest { setDeduplicationGroup(DEDUPLICATION_GROUP) addPackageCertificateHash(HASH1) } + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } } .build() ) @@ -436,6 +472,9 @@ class SafetySourceTest { setDeduplicationGroup(DEDUPLICATION_GROUP) addPackageCertificateHash(HASH1) } + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } } .build() ) @@ -459,6 +498,9 @@ class SafetySourceTest { setDeduplicationGroup(DEDUPLICATION_GROUP) addPackageCertificateHash(HASH1) } + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } } .build() ) @@ -482,6 +524,9 @@ class SafetySourceTest { setDeduplicationGroup(DEDUPLICATION_GROUP) addPackageCertificateHash(HASH1) } + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } } .build() ) @@ -505,6 +550,9 @@ class SafetySourceTest { setDeduplicationGroup(DEDUPLICATION_GROUP) addPackageCertificateHash(HASH1) } + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } } .build() ) @@ -536,6 +584,9 @@ class SafetySourceTest { setDeduplicationGroup(DEDUPLICATION_GROUP) addPackageCertificateHash(HASH1) } + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } } .build() ) @@ -559,6 +610,9 @@ class SafetySourceTest { setDeduplicationGroup(DEDUPLICATION_GROUP) addPackageCertificateHash(HASH1) } + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } } .build() ) @@ -582,6 +636,9 @@ class SafetySourceTest { setDeduplicationGroup(DEDUPLICATION_GROUP) addPackageCertificateHash(HASH1) } + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } } .build() ) @@ -605,6 +662,9 @@ class SafetySourceTest { setDeduplicationGroup(DEDUPLICATION_GROUP) addPackageCertificateHash(HASH1) } + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } } .build() ) @@ -628,6 +688,9 @@ class SafetySourceTest { setDeduplicationGroup(DEDUPLICATION_GROUP) addPackageCertificateHash(HASH1) } + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } } .build() ) @@ -650,6 +713,11 @@ class SafetySourceTest { .setNotificationsAllowed(false) .setDeduplicationGroup(DEDUPLICATION_GROUP) .addPackageCertificateHash(HASH1) + .apply { + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } + } .build() ) addEqualityGroup( @@ -669,6 +737,11 @@ class SafetySourceTest { .setNotificationsAllowed(true) .setDeduplicationGroup("other_deduplication_group") .addPackageCertificateHash(HASH1) + .apply { + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } + } .build() ) // With no package cert hashes provided @@ -688,6 +761,11 @@ class SafetySourceTest { .setRefreshOnPageOpenAllowed(true) .setNotificationsAllowed(true) .setDeduplicationGroup(DEDUPLICATION_GROUP) + .apply { + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } + } .build() ) // With longer package cert hash list @@ -709,6 +787,11 @@ class SafetySourceTest { .setDeduplicationGroup(DEDUPLICATION_GROUP) .addPackageCertificateHash(HASH1) .addPackageCertificateHash(HASH2) + .apply { + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } + } .build() ) // With package cert hash list with different value @@ -729,6 +812,11 @@ class SafetySourceTest { .setNotificationsAllowed(true) .setDeduplicationGroup(DEDUPLICATION_GROUP) .addPackageCertificateHash(HASH2) + .apply { + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } + } .build() ) } @@ -785,6 +873,9 @@ class SafetySourceTest { setDeduplicationGroup(DEDUPLICATION_GROUP) addPackageCertificateHash(HASH1) } + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } } .build() @@ -817,6 +908,11 @@ class SafetySourceTest { .setProfile(SafetySource.PROFILE_ALL) .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN) .setSearchTermsResId(REFERENCE_RES_ID) + .apply { + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } + } .build() internal val STATIC_BAREBONE = @@ -837,6 +933,11 @@ class SafetySourceTest { .setIntentAction(INTENT_ACTION) .setProfile(SafetySource.PROFILE_ALL) .setSearchTermsResId(REFERENCE_RES_ID) + .apply { + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(REFERENCE_RES_ID) + } + } .build() internal val ISSUE_ONLY_BAREBONE = diff --git a/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt b/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt index 7623af950..2eb1a6c48 100644 --- a/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt +++ b/tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt @@ -283,11 +283,7 @@ class SafetyCenterMultiUsersTest { .safetyCenterEntryDefaultStaticBuilder( STATIC_ALL_OPTIONAL_ID, userId = deviceState.privateProfile().id(), - // TODO(b/286539356): Change this to a profile specific - // title when private profile starts reading the title from - // the config - currently it relies on the primary profile's - // title - title = "OK" + title = "Unknown" ) .setPendingIntent( createTestActivityRedirectPendingIntentForUser( @@ -325,11 +321,7 @@ class SafetyCenterMultiUsersTest { ) private fun staticEntryForPrivateBuilder( - // TODO(b/286539356): Change this to a profile specific - // title when private profile starts reading the title from - // the config - currently it relies on the primary profile's - // title - title: CharSequence = "OK", + title: CharSequence = "Unknown", explicit: Boolean = true ) = SafetyCenterStaticEntry.Builder(title) @@ -949,6 +941,7 @@ class SafetyCenterMultiUsersTest { val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission() val managedUserId = deviceState.workProfile().id() + val privateProfileId = deviceState.privateProfile().id() val safetyCenterDataFromComplexConfig = SafetyCenterData( safetyCenterTestData.safetyCenterStatusCritical(11), @@ -1004,6 +997,32 @@ class SafetyCenterMultiUsersTest { ISSUE_ONLY_IN_STATELESS_ID, managedUserId, groupId = MIXED_STATELESS_GROUP_ID + ), + safetyCenterTestData.safetyCenterIssueInformation( + DYNAMIC_DISABLED_ID, + privateProfileId, + groupId = DYNAMIC_GROUP_ID + ), + safetyCenterTestData.safetyCenterIssueInformation( + DYNAMIC_HIDDEN_ID, + privateProfileId, + groupId = DYNAMIC_GROUP_ID + ), + safetyCenterTestData.safetyCenterIssueInformation( + ISSUE_ONLY_ALL_OPTIONAL_ID, + privateProfileId, + attributionTitle = null, + groupId = ISSUE_ONLY_GROUP_ID + ), + safetyCenterTestData.safetyCenterIssueInformation( + DYNAMIC_IN_STATELESS_ID, + privateProfileId, + groupId = MIXED_STATELESS_GROUP_ID + ), + safetyCenterTestData.safetyCenterIssueInformation( + ISSUE_ONLY_IN_STATELESS_ID, + privateProfileId, + groupId = MIXED_STATELESS_GROUP_ID ) ), listOf( @@ -1238,11 +1257,7 @@ class SafetyCenterMultiUsersTest { safetyCenterTestData.safetyCenterEntryDefault( SINGLE_SOURCE_ALL_PROFILE_ID, deviceState.privateProfile().id(), - // TODO(b/286539356): Change this to a profile specific - // title when private profile starts reading the title from - // the config - currently it relies on the primary profile's - // title - title = "OK", + title = "Unknown", pendingIntent = createTestActivityRedirectPendingIntentForUser( deviceState.privateProfile().userHandle() @@ -1259,14 +1274,14 @@ class SafetyCenterMultiUsersTest { emptyList() ) - // TODO(b/286539356): Add checks for the SafetyCenterData once that's adapted for the - // private profile. - assertThat(safetyCenterManager.getSafetyCenterDataWithPermission()) - .isEqualTo(safetyCenterDataWithPrivateProfile) - assertThat( - privateSafetyCenterManager.getSafetyCenterDataWithInteractAcrossUsersPermission() - ) - .isEqualTo(safetyCenterDataWithPrivateProfile) + checkState( + safetyCenterManager.getSafetyCenterDataWithPermission() == + safetyCenterDataWithPrivateProfile + ) + checkState( + privateSafetyCenterManager.getSafetyCenterDataWithInteractAcrossUsersPermission() == + safetyCenterDataWithPrivateProfile + ) deviceState.privateProfile().remove() diff --git a/tests/utils/safetycenter/Android.bp b/tests/utils/safetycenter/Android.bp index 6accefae9..8514b0662 100644 --- a/tests/utils/safetycenter/Android.bp +++ b/tests/utils/safetycenter/Android.bp @@ -36,6 +36,7 @@ android_library { "kotlinx-coroutines-android", "safety-center-internal-data", "safety-center-resources-lib", + "permissions-aconfig-flags-lib", ], apex_available: [ "com.android.permission", diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt index fd3749094..a4600e88b 100644 --- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt +++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt @@ -31,6 +31,7 @@ import android.safetycenter.config.SafetySource.SAFETY_SOURCE_TYPE_STATIC import android.safetycenter.config.SafetySourcesGroup import androidx.annotation.RequiresApi import com.android.modules.utils.build.SdkLevel +import com.android.permission.flags.Flags import com.android.safetycenter.testing.SettingsPackage.getSettingsPackageName import java.security.MessageDigest @@ -686,6 +687,11 @@ class SafetyCenterTestConfigs(private val context: Context) { .setSummaryResId(Resources.ID_NULL) .setIntentAction(null) .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN) + .apply { + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(Resources.ID_NULL) + } + } .build() ) .build() @@ -788,6 +794,11 @@ class SafetyCenterTestConfigs(private val context: Context) { dynamicSafetySourceBuilder(id) .setProfile(SafetySource.PROFILE_ALL) .setTitleForWorkResId(android.R.string.paste) + .apply { + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(android.R.string.paste) + } + } private fun staticSafetySource(id: String) = staticSafetySourceBuilder(id).build() @@ -803,6 +814,11 @@ class SafetyCenterTestConfigs(private val context: Context) { staticSafetySourceBuilder(id) .setProfile(SafetySource.PROFILE_ALL) .setTitleForWorkResId(android.R.string.paste) + .apply { + if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) { + setTitleForPrivateProfileResId(android.R.string.unknownName) + } + } @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) private fun issueOnlySafetySourceWithDuplicationInfo(id: String, deduplicationGroup: String) = |