summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Manish Singh <psych@google.com> 2024-02-08 12:55:40 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-02-08 12:55:40 +0000
commitfcba235c4c03c0bf0b58e8b1661be45eec7d57ca (patch)
treef0a2d001ad75140572f9a5d7bf43e0547f362cb6
parenta78527583c737cd4c04535533a9dae6df9f6951b (diff)
parent1103d313bf297370cd948cf4a78d4a3f748ba87e (diff)
Merge changes from topics "ps_safety_center", "sc_biometrics", "sc_private_profile_api" into main
* changes: Add the titleForPrivateProfile API Generalise the code for various profile types - 6 Generalise the code for various profile types - 5 Generalise the code for various profile types - 4 Don't add primary profile in default group summary Generalise the code for various profile types - 3 Generalise the code for various profile types - 2 Generalise the code for various profile types - 1
-rw-r--r--SafetyCenter/Config/Android.bp1
-rw-r--r--SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java25
-rw-r--r--SafetyCenter/Config/tests/Android.bp1
-rw-r--r--SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt57
-rw-r--r--SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt16
-rw-r--r--SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_disabled_no_private.xml18
-rw-r--r--SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_hidden_with_search_no_private.xml17
-rw-r--r--SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_no_private.xml17
-rw-r--r--SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_primary_hidden_with_private.xml15
-rw-r--r--SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_primary_with_private.xml17
-rw-r--r--SafetyCenter/Config/tests/res/raw-v35/config_issue_only_safety_source_with_private.xml12
-rw-r--r--SafetyCenter/Config/tests/res/raw-v35/config_static_safety_source_with_primary_and_private.xml16
-rw-r--r--SafetyCenter/Config/tests/res/raw-v35/config_valid.xml197
-rw-r--r--SafetyCenter/Config/tests/res/raw/config_static_safety_source_with_private_profile.xml17
-rw-r--r--SafetyCenter/ConfigLintChecker/Android.bp119
-rw-r--r--SafetyCenter/Resources/Android.bp7
-rw-r--r--SafetyCenter/Resources/res/raw-v35/safety_center_config.xml1
-rw-r--r--SafetyCenter/Resources/res/values-v35/strings.xml3
-rw-r--r--flags/flags.aconfig16
-rw-r--r--framework-s/api/system-current.txt2
-rw-r--r--framework-s/java/android/safetycenter/config/SafetySource.java100
-rw-r--r--framework-s/java/android/safetycenter/config/safety_center_config-v35.xsd223
-rw-r--r--service/java/com/android/permission/util/UserUtils.java35
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java49
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterConfigReader.java91
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterDataFactory.java228
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterListeners.java4
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterRefreshTracker.java5
-rw-r--r--service/java/com/android/safetycenter/SafetySources.java19
-rw-r--r--service/java/com/android/safetycenter/UserProfileGroup.java219
-rw-r--r--service/java/com/android/safetycenter/data/SafetyCenterDataManager.java5
-rw-r--r--service/java/com/android/safetycenter/data/SafetyCenterInFlightIssueActionRepository.java4
-rw-r--r--service/java/com/android/safetycenter/data/SafetyCenterIssueRepository.java25
-rw-r--r--service/java/com/android/safetycenter/data/SafetySourceDataValidator.java12
-rw-r--r--service/java/com/android/safetycenter/data/SafetySourceStateCollectedLogger.java13
-rw-r--r--service/java/com/android/safetycenter/logging/SafetyCenterPullAtomCallback.java31
-rw-r--r--service/java/com/android/safetycenter/logging/SafetyCenterStatsdLogger.java82
-rw-r--r--service/java/com/android/safetycenter/notifications/SafetyCenterNotificationReceiver.java5
-rw-r--r--service/java/com/android/safetycenter/notifications/SafetyCenterNotificationSender.java11
-rw-r--r--tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt101
-rw-r--r--tests/functional/safetycenter/multiusers/Android.bp1
-rw-r--r--tests/functional/safetycenter/multiusers/src/android/safetycenter/functional/multiusers/SafetyCenterMultiUsersTest.kt417
-rw-r--r--tests/utils/safetycenter/Android.bp1
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt16
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt36
45 files changed, 1951 insertions, 356 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 00f7c2ada..8bf3a63ab 100644
--- a/flags/flags.aconfig
+++ b/flags/flags.aconfig
@@ -7,6 +7,22 @@ flag {
bug: "292252664"
}
+flag {
+ name: "private_profile_supported"
+ namespace: "permissions"
+ description: "This flag is used to support private profile in safety center"
+ bug: "286539356"
+ 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 527407bf3..61af606e9 100644
--- a/framework-s/api/system-current.txt
+++ b/framework-s/api/system-current.txt
@@ -571,6 +571,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();
@@ -606,6 +607,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/permission/util/UserUtils.java b/service/java/com/android/permission/util/UserUtils.java
index 8205be239..33389a88f 100644
--- a/service/java/com/android/permission/util/UserUtils.java
+++ b/service/java/com/android/permission/util/UserUtils.java
@@ -25,7 +25,9 @@ import android.os.UserHandle;
import android.os.UserManager;
import com.android.internal.util.Preconditions;
+import com.android.modules.utils.build.SdkLevel;
import com.android.permission.compat.UserHandleCompat;
+import com.android.permission.flags.Flags;
import java.util.List;
@@ -92,6 +94,39 @@ public final class UserUtils {
}
/**
+ * Returns whether the given {@code userId} is a private profile. Note that private profiles are
+ * allowed from Android V+ only, so this method will return false on Sdk levels below that.
+ */
+ public static boolean isPrivateProfile(@UserIdInt int userId, @NonNull Context context) {
+ if (!isPrivateProfileSupported()) {
+ return false;
+ }
+ // It's needed to clear the calling identity because we are going to query the UserManager
+ // for isPrivateProfile() and Context for createContextAsUser, which requires one of the
+ // following permissions:
+ // MANAGE_USERS, QUERY_USERS, or INTERACT_ACROSS_USERS.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Context userContext = context
+ .createContextAsUser(UserHandle.of(userId), /* flags= */ 0);
+ UserManager userManager = userContext.getSystemService(UserManager.class);
+ return userManager != null && userManager.isPrivateProfile();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+
+ /**
+ * Returns whether private profile's allowed to exist. This can be true iff the SdkLevel is at
+ * least V AND the permission module's private profile feature flag is enabled.
+ */
+ public static boolean isPrivateProfileSupported() {
+ //TODO(b/286539356) add the os feature flag protection when available.
+ return SdkLevel.isAtLeastV() && Flags.privateProfileSupported();
+ }
+
+ /**
* Returns whether a given {@code userId} corresponds to a running managed profile, i.e. the
* user is running and the quiet mode is not enabled.
*/
diff --git a/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java b/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java
index a36beb2d3..5c9dfa664 100644
--- a/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java
+++ b/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java
@@ -49,6 +49,7 @@ import androidx.annotation.Nullable;
import com.android.permission.util.PackageUtils;
import com.android.safetycenter.SafetyCenterConfigReader.Broadcast;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
import com.android.safetycenter.data.SafetyCenterDataManager;
import java.time.Duration;
@@ -334,37 +335,35 @@ final class SafetyCenterBroadcastDispatcher {
* lists of source IDs.
*
* <p>The set of user IDs (keys) is the profile parent user ID of {@code userProfileGroup} plus
- * the (possibly empty) set of running managed profile user IDs in that group.
- *
+ * all the other types of running profiles:
+ * <ol>
+ * <li>The (possibly empty) set of running managed profile user IDs in that group.
+ * <li>The (possibly empty) set of running private profile user ID in that group.
+ * </ol>
* <p>Every value present is a non-empty list, but the overall result may be empty.
*/
private SparseArray<List<String>> getUserIdsToSourceIds(
Broadcast broadcast,
UserProfileGroup userProfileGroup,
@RefreshReason int refreshReason) {
- int[] managedProfileIds = userProfileGroup.getManagedRunningProfilesUserIds();
- SparseArray<List<String>> result = new SparseArray<>(managedProfileIds.length + 1);
- List<String> profileParentSources =
- getSourceIdsForRefreshReason(
- refreshReason,
- broadcast.getSourceIdsForProfileParent(),
- broadcast.getSourceIdsForProfileParentOnPageOpen(),
- userProfileGroup.getProfileParentUserId());
-
- if (!profileParentSources.isEmpty()) {
- result.put(userProfileGroup.getProfileParentUserId(), profileParentSources);
- }
-
- for (int i = 0; i < managedProfileIds.length; i++) {
- List<String> managedProfileSources =
- getSourceIdsForRefreshReason(
- refreshReason,
- broadcast.getSourceIdsForManagedProfiles(),
- broadcast.getSourceIdsForManagedProfilesOnPageOpen(),
- managedProfileIds[i]);
-
- if (!managedProfileSources.isEmpty()) {
- result.put(managedProfileIds[i], managedProfileSources);
+ SparseArray<List<String>> result =
+ new SparseArray<>(userProfileGroup.getNumRunningProfiles());
+ for (int profilTypeIdx = 0;
+ profilTypeIdx < ProfileType.ALL_PROFILE_TYPES.length;
+ ++profilTypeIdx) {
+ @ProfileType int profileType = ProfileType.ALL_PROFILE_TYPES[profilTypeIdx];
+ int[] runningProfiles = userProfileGroup.getRunningProfilesOfType(profileType);
+ for (int profileIdx = 0; profileIdx < runningProfiles.length; ++profileIdx) {
+ List<String> profileSources =
+ getSourceIdsForRefreshReason(
+ refreshReason,
+ broadcast.getSourceIdsForProfileType(profileType),
+ broadcast.getSourceIdsOnPageOpenForProfileType(profileType),
+ runningProfiles[profileIdx]);
+
+ if (!profileSources.isEmpty()) {
+ result.put(runningProfiles[profileIdx], profileSources);
+ }
}
}
diff --git a/service/java/com/android/safetycenter/SafetyCenterConfigReader.java b/service/java/com/android/safetycenter/SafetyCenterConfigReader.java
index c473ad916..641c242f1 100644
--- a/service/java/com/android/safetycenter/SafetyCenterConfigReader.java
+++ b/service/java/com/android/safetycenter/SafetyCenterConfigReader.java
@@ -16,6 +16,11 @@
package com.android.safetycenter;
+import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_MANAGED;
+import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_PRIMARY;
+import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_PRIVATE;
+
+import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static java.util.Objects.requireNonNull;
@@ -28,6 +33,7 @@ import android.util.Log;
import androidx.annotation.Nullable;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
import com.android.safetycenter.config.ParseException;
import com.android.safetycenter.config.SafetyCenterConfigParser;
import com.android.safetycenter.resources.SafetyCenterResourcesApk;
@@ -410,7 +416,7 @@ public final class SafetyCenterConfigReader {
broadcast.mSourceIdsForProfileParentOnPageOpen.add(safetySource.getId());
}
boolean needsManagedProfilesBroadcast =
- SafetySources.supportsManagedProfiles(safetySource);
+ SafetySources.supportsProfileType(safetySource, PROFILE_TYPE_MANAGED);
if (needsManagedProfilesBroadcast) {
broadcast.mSourceIdsForManagedProfiles.add(safetySource.getId());
if (safetySource.isRefreshOnPageOpenAllowed()) {
@@ -418,6 +424,19 @@ public final class SafetyCenterConfigReader {
safetySource.getId());
}
}
+
+ // TODO(b/317378205): think about generalising these fields in Broadcast so that
+ // we are not duplicating the code - it can be a source of confusion and errors
+ // in future.
+ boolean needsPrivateProfileBroadcast =
+ SafetySources.supportsProfileType(safetySource, PROFILE_TYPE_PRIVATE);
+ if (needsPrivateProfileBroadcast) {
+ broadcast.mSourceIdsForPrivateProfile.add(safetySource.getId());
+ if (safetySource.isRefreshOnPageOpenAllowed()) {
+ broadcast.mSourceIdsForPrivateProfileOnPageOpen.add(
+ safetySource.getId());
+ }
+ }
}
}
@@ -486,6 +505,8 @@ public final class SafetyCenterConfigReader {
private final List<String> mSourceIdsForProfileParentOnPageOpen = new ArrayList<>();
private final List<String> mSourceIdsForManagedProfiles = new ArrayList<>();
private final List<String> mSourceIdsForManagedProfilesOnPageOpen = new ArrayList<>();
+ private final List<String> mSourceIdsForPrivateProfile = new ArrayList<>();
+ private final List<String> mSourceIdsForPrivateProfileOnPageOpen = new ArrayList<>();
private Broadcast(String packageName) {
mPackageName = packageName;
@@ -497,41 +518,42 @@ public final class SafetyCenterConfigReader {
}
/**
- * Returns the safety source ids associated with this broadcast in the profile owner.
- *
- * <p>If this list is empty, there are no sources to dispatch to in the profile owner.
- */
- List<String> getSourceIdsForProfileParent() {
- return unmodifiableList(mSourceIdsForProfileParent);
- }
-
- /**
- * Returns the safety source ids associated with this broadcast in the profile owner that
- * have refreshOnPageOpenAllowed set to true in the XML config.
+ * Returns the safety source ids associated with this broadcast in the given profile type.
*
- * <p>If this list is empty, there are no sources to dispatch to in the profile owner.
+ * <p>If this list is empty, there are no sources to dispatch to in the given profile type.
*/
- List<String> getSourceIdsForProfileParentOnPageOpen() {
- return unmodifiableList(mSourceIdsForProfileParentOnPageOpen);
- }
-
- /**
- * Returns the safety source ids associated with this broadcast in the managed profile(s).
- *
- * <p>If this list is empty, there are no sources to dispatch to in the managed profile(s).
- */
- List<String> getSourceIdsForManagedProfiles() {
- return unmodifiableList(mSourceIdsForManagedProfiles);
+ List<String> getSourceIdsForProfileType(@ProfileType int profileType) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return unmodifiableList(mSourceIdsForProfileParent);
+ case PROFILE_TYPE_MANAGED:
+ return unmodifiableList(mSourceIdsForManagedProfiles);
+ case PROFILE_TYPE_PRIVATE:
+ return unmodifiableList(mSourceIdsForPrivateProfile);
+ default:
+ Log.w(TAG, "source ids asked for unexpected profile " + profileType);
+ return emptyList();
+ }
}
/**
- * Returns the safety source ids associated with this broadcast in the managed profile(s)
+ * Returns the safety source ids associated with this broadcast in the given profile type
* that have refreshOnPageOpenAllowed set to true in the XML config.
*
- * <p>If this list is empty, there are no sources to dispatch to in the managed profile(s).
+ * <p>If this list is empty, there are no sources to dispatch to in the given profile type.
*/
- List<String> getSourceIdsForManagedProfilesOnPageOpen() {
- return unmodifiableList(mSourceIdsForManagedProfilesOnPageOpen);
+ List<String> getSourceIdsOnPageOpenForProfileType(@ProfileType int profileType) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return unmodifiableList(mSourceIdsForProfileParentOnPageOpen);
+ case PROFILE_TYPE_MANAGED:
+ return unmodifiableList(mSourceIdsForManagedProfilesOnPageOpen);
+ case PROFILE_TYPE_PRIVATE:
+ return unmodifiableList(mSourceIdsForPrivateProfileOnPageOpen);
+ default:
+ Log.w(TAG, "source ids asked for unexpected profile " + profileType);
+ return emptyList();
+ }
}
@Override
@@ -545,7 +567,10 @@ public final class SafetyCenterConfigReader {
that.mSourceIdsForProfileParentOnPageOpen)
&& mSourceIdsForManagedProfiles.equals(that.mSourceIdsForManagedProfiles)
&& mSourceIdsForManagedProfilesOnPageOpen.equals(
- that.mSourceIdsForManagedProfilesOnPageOpen);
+ that.mSourceIdsForManagedProfilesOnPageOpen)
+ && mSourceIdsForPrivateProfile.equals(that.mSourceIdsForPrivateProfile)
+ && mSourceIdsForPrivateProfileOnPageOpen.equals(
+ that.mSourceIdsForPrivateProfileOnPageOpen);
}
@Override
@@ -555,7 +580,9 @@ public final class SafetyCenterConfigReader {
mSourceIdsForProfileParent,
mSourceIdsForProfileParentOnPageOpen,
mSourceIdsForManagedProfiles,
- mSourceIdsForManagedProfilesOnPageOpen);
+ mSourceIdsForManagedProfilesOnPageOpen,
+ mSourceIdsForPrivateProfile,
+ mSourceIdsForPrivateProfileOnPageOpen);
}
@Override
@@ -571,6 +598,10 @@ public final class SafetyCenterConfigReader {
+ mSourceIdsForManagedProfiles
+ ", mSourceIdsForManagedProfilesOnPageOpen="
+ mSourceIdsForManagedProfilesOnPageOpen
+ + ", mSourceIdsForPrivateProfile="
+ + mSourceIdsForPrivateProfile
+ + ", mSourceIdsForPrivateProfileOnPageOpen="
+ + mSourceIdsForPrivateProfileOnPageOpen
+ '}';
}
}
diff --git a/service/java/com/android/safetycenter/SafetyCenterDataFactory.java b/service/java/com/android/safetycenter/SafetyCenterDataFactory.java
index d74d160f4..7c7ade3f1 100644
--- a/service/java/com/android/safetycenter/SafetyCenterDataFactory.java
+++ b/service/java/com/android/safetycenter/SafetyCenterDataFactory.java
@@ -17,7 +17,11 @@
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;
+import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_PRIVATE;
import static com.android.safetycenter.internaldata.SafetyCenterBundles.ISSUES_TO_GROUPS_BUNDLE_KEY;
import static com.android.safetycenter.internaldata.SafetyCenterBundles.STATIC_ENTRIES_TO_IDS_BUNDLE_KEY;
@@ -53,7 +57,8 @@ import android.util.Log;
import androidx.annotation.Nullable;
import com.android.modules.utils.build.SdkLevel;
-import com.android.permission.util.UserUtils;
+import com.android.permission.flags.Flags;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
import com.android.safetycenter.data.SafetyCenterDataManager;
import com.android.safetycenter.internaldata.SafetyCenterBundles;
import com.android.safetycenter.internaldata.SafetyCenterEntryId;
@@ -387,42 +392,36 @@ public final class SafetyCenterDataFactory {
List<SafetySource> safetySources = safetySourcesGroup.getSafetySources();
List<SafetyCenterEntry> entries = new ArrayList<>(safetySources.size());
- for (int i = 0; i < safetySources.size(); i++) {
- SafetySource safetySource = safetySources.get(i);
-
- groupSafetyCenterEntryLevel =
- mergeSafetyCenterEntrySeverityLevels(
- groupSafetyCenterEntryLevel,
- addSafetyCenterEntry(
- safetyCenterOverallState,
- entries,
- safetySource,
- defaultPackageName,
- userProfileGroup.getProfileParentUserId(),
- /* isUserManaged= */ false,
- /* isManagedUserRunning= */ false));
-
- if (!SafetySources.supportsManagedProfiles(safetySource)) {
- continue;
- }
-
- int[] managedProfilesUserIds = userProfileGroup.getManagedProfilesUserIds();
- for (int j = 0; j < managedProfilesUserIds.length; j++) {
- int managedProfileUserId = managedProfilesUserIds[j];
- boolean isManagedUserRunning =
- userProfileGroup.isManagedUserRunning(managedProfileUserId);
+ for (int safetySourceIdx = 0; safetySourceIdx < safetySources.size(); ++safetySourceIdx) {
+ SafetySource safetySource = safetySources.get(safetySourceIdx);
+ for (int profileTypeIdx = 0;
+ profileTypeIdx < ProfileType.ALL_PROFILE_TYPES.length;
+ ++profileTypeIdx) {
+ @ProfileType int profileType = ProfileType.ALL_PROFILE_TYPES[profileTypeIdx];
+ if (!SafetySources.supportsProfileType(safetySource, profileType)) {
+ continue;
+ }
- groupSafetyCenterEntryLevel =
- mergeSafetyCenterEntrySeverityLevels(
- groupSafetyCenterEntryLevel,
- addSafetyCenterEntry(
- safetyCenterOverallState,
- entries,
- safetySource,
- defaultPackageName,
- managedProfileUserId,
- /* isUserManaged= */ true,
- isManagedUserRunning));
+ int[] profileIds = userProfileGroup.getProfilesOfType(profileType);
+ for (int profileIdx = 0; profileIdx < profileIds.length; profileIdx++) {
+ int profileId = profileIds[profileIdx];
+ boolean isUserRunning =
+ userProfileGroup.containsRunningUserId(profileId, profileType);
+ if (profileType == PROFILE_TYPE_PRIVATE && !isUserRunning) {
+ continue;
+ }
+ groupSafetyCenterEntryLevel =
+ mergeSafetyCenterEntrySeverityLevels(
+ groupSafetyCenterEntryLevel,
+ addSafetyCenterEntry(
+ safetyCenterOverallState,
+ entries,
+ safetySource,
+ defaultPackageName,
+ profileId,
+ profileType,
+ isUserRunning));
+ }
}
}
@@ -534,7 +533,8 @@ public final class SafetyCenterDataFactory {
SafetyCenterEntry entry = entries.get(i);
SafetyCenterEntryId entryId = SafetyCenterIds.entryIdFromString(entry.getId());
- if (UserUtils.isManagedProfile(entryId.getUserId(), mContext)) {
+ if (UserProfileGroup.getProfileTypeOfUser(entryId.getUserId(), mContext)
+ != PROFILE_TYPE_PRIMARY) {
continue;
}
@@ -558,15 +558,15 @@ public final class SafetyCenterDataFactory {
SafetySource safetySource,
String defaultPackageName,
@UserIdInt int userId,
- boolean isUserManaged,
- boolean isManagedUserRunning) {
+ @ProfileType int profileType,
+ boolean isUserRunning) {
SafetyCenterEntry safetyCenterEntry =
toSafetyCenterEntry(
safetySource,
defaultPackageName,
userId,
- isUserManaged,
- isManagedUserRunning);
+ profileType,
+ isUserRunning);
if (safetyCenterEntry == null) {
return SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED;
}
@@ -584,8 +584,8 @@ public final class SafetyCenterDataFactory {
SafetySource safetySource,
String defaultPackageName,
@UserIdInt int userId,
- boolean isUserManaged,
- boolean isManagedUserRunning) {
+ @ProfileType int profileType,
+ boolean isUserRunning) {
switch (safetySource.getType()) {
case SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY:
return null;
@@ -594,7 +594,7 @@ public final class SafetyCenterDataFactory {
SafetySourceStatus safetySourceStatus =
getSafetySourceStatus(
mSafetyCenterDataManager.getSafetySourceDataInternal(key));
- boolean inQuietMode = isUserManaged && !isManagedUserRunning;
+ boolean inQuietMode = (PROFILE_TYPE_MANAGED == profileType) && !isUserRunning;
if (safetySourceStatus == null) {
int severityLevel =
inQuietMode
@@ -606,8 +606,8 @@ public final class SafetyCenterDataFactory {
severityLevel,
SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION,
userId,
- isUserManaged,
- isManagedUserRunning);
+ profileType,
+ isUserRunning);
}
PendingIntent sourceProvidedPendingIntent =
inQuietMode ? null : safetySourceStatus.getPendingIntent();
@@ -665,8 +665,8 @@ public final class SafetyCenterDataFactory {
SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED,
SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON,
userId,
- isUserManaged,
- isManagedUserRunning);
+ profileType,
+ isUserRunning);
}
Log.w(
TAG,
@@ -681,8 +681,8 @@ public final class SafetyCenterDataFactory {
@SafetyCenterEntry.EntrySeverityLevel int entrySeverityLevel,
@SafetyCenterEntry.SeverityUnspecifiedIconType int severityUnspecifiedIconType,
@UserIdInt int userId,
- boolean isUserManaged,
- boolean isManagedUserRunning) {
+ @ProfileType int profileType,
+ boolean isUserRunning) {
if (SafetySources.isDefaultEntryHidden(safetySource)) {
return null;
}
@@ -692,7 +692,7 @@ public final class SafetyCenterDataFactory {
.setSafetySourceId(safetySource.getId())
.setUserId(userId)
.build();
- boolean isQuietModeEnabled = isUserManaged && !isManagedUserRunning;
+ boolean isQuietModeEnabled = (PROFILE_TYPE_MANAGED == profileType) && !isUserRunning;
PendingIntent pendingIntent =
mPendingIntentFactory.getPendingIntent(
safetySource.getId(),
@@ -702,13 +702,7 @@ public final class SafetyCenterDataFactory {
isQuietModeEnabled);
boolean enabled =
pendingIntent != null && !SafetySources.isDefaultEntryDisabled(safetySource);
- CharSequence title =
- isUserManaged
- ? DevicePolicyResources.getSafetySourceWorkString(
- mSafetyCenterResourcesApk,
- safetySource.getId(),
- safetySource.getTitleForWorkResId())
- : mSafetyCenterResourcesApk.getString(safetySource.getTitleResId());
+ CharSequence title = getTitleForProfileType(profileType, safetySource);
CharSequence summary =
mSafetyCenterDataManager.sourceHasError(
SafetySourceKey.of(safetySource.getId(), userId))
@@ -738,38 +732,33 @@ public final class SafetyCenterDataFactory {
UserProfileGroup userProfileGroup) {
List<SafetySource> safetySources = safetySourcesGroup.getSafetySources();
List<SafetyCenterStaticEntry> staticEntries = new ArrayList<>(safetySources.size());
- for (int i = 0; i < safetySources.size(); i++) {
- SafetySource safetySource = safetySources.get(i);
-
- addSafetyCenterStaticEntry(
- staticEntriesToIds,
- safetyCenterOverallState,
- staticEntries,
- safetySource,
- defaultPackageName,
- userProfileGroup.getProfileParentUserId(),
- /* isUserManaged= */ false,
- /* isManagedUserRunning= */ false);
-
- if (!SafetySources.supportsManagedProfiles(safetySource)) {
- continue;
- }
-
- int[] managedProfilesUserIds = userProfileGroup.getManagedProfilesUserIds();
- for (int j = 0; j < managedProfilesUserIds.length; j++) {
- int managedProfileUserId = managedProfilesUserIds[j];
- boolean isManagedUserRunning =
- userProfileGroup.isManagedUserRunning(managedProfileUserId);
-
- addSafetyCenterStaticEntry(
- staticEntriesToIds,
- safetyCenterOverallState,
- staticEntries,
- safetySource,
- defaultPackageName,
- managedProfileUserId,
- /* isUserManaged= */ true,
- isManagedUserRunning);
+ for (int safetySourceIdx = 0; safetySourceIdx < safetySources.size(); safetySourceIdx++) {
+ SafetySource safetySource = safetySources.get(safetySourceIdx);
+ for (int profileTypeIdx = 0;
+ profileTypeIdx < ProfileType.ALL_PROFILE_TYPES.length;
+ ++profileTypeIdx) {
+ @ProfileType int profileType = ProfileType.ALL_PROFILE_TYPES[profileTypeIdx];
+ if (!SafetySources.supportsProfileType(safetySource, profileType)) {
+ continue;
+ }
+ int[] profileIds = userProfileGroup.getProfilesOfType(profileType);
+ for (int profileIdx = 0; profileIdx < profileIds.length; ++profileIdx) {
+ int profileId = profileIds[profileIdx];
+ boolean isUserRunning =
+ userProfileGroup.containsRunningUserId(profileId, profileType);
+ if (profileType == PROFILE_TYPE_PRIVATE && !isUserRunning) {
+ continue;
+ }
+ addSafetyCenterStaticEntry(
+ staticEntriesToIds,
+ safetyCenterOverallState,
+ staticEntries,
+ safetySource,
+ defaultPackageName,
+ profileId,
+ profileType,
+ isUserRunning);
+ }
}
}
@@ -790,15 +779,15 @@ public final class SafetyCenterDataFactory {
SafetySource safetySource,
String defaultPackageName,
@UserIdInt int userId,
- boolean isUserManaged,
- boolean isManagedUserRunning) {
+ @ProfileType int profileType,
+ boolean isUserRunning) {
SafetyCenterStaticEntry staticEntry =
toSafetyCenterStaticEntry(
safetySource,
defaultPackageName,
userId,
- isUserManaged,
- isManagedUserRunning);
+ profileType,
+ isUserRunning);
if (staticEntry == null) {
return;
}
@@ -826,8 +815,8 @@ public final class SafetyCenterDataFactory {
SafetySource safetySource,
String defaultPackageName,
@UserIdInt int userId,
- boolean isUserManaged,
- boolean isManagedUserRunning) {
+ @ProfileType int profileType,
+ boolean isUserRunning) {
switch (safetySource.getType()) {
case SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY:
return null;
@@ -836,7 +825,7 @@ public final class SafetyCenterDataFactory {
SafetySourceStatus safetySourceStatus =
getSafetySourceStatus(
mSafetyCenterDataManager.getSafetySourceDataInternal(key));
- boolean inQuietMode = isUserManaged && !isManagedUserRunning;
+ boolean inQuietMode = (profileType == PROFILE_TYPE_MANAGED) && !isUserRunning;
if (safetySourceStatus != null) {
PendingIntent sourceProvidedPendingIntent =
inQuietMode ? null : safetySourceStatus.getPendingIntent();
@@ -867,15 +856,15 @@ public final class SafetyCenterDataFactory {
safetySource,
safetySource.getPackageName(),
userId,
- isUserManaged,
- isManagedUserRunning);
+ profileType,
+ isUserRunning);
case SafetySource.SAFETY_SOURCE_TYPE_STATIC:
return toDefaultSafetyCenterStaticEntry(
safetySource,
getStaticSourcePackageNameOrDefault(safetySource, defaultPackageName),
userId,
- isUserManaged,
- isManagedUserRunning);
+ profileType,
+ isUserRunning);
}
Log.w(TAG, "Unknown safety source type found in rigid group: " + safetySource.getType());
return null;
@@ -886,12 +875,12 @@ public final class SafetyCenterDataFactory {
SafetySource safetySource,
String packageName,
@UserIdInt int userId,
- boolean isUserManaged,
- boolean isManagedUserRunning) {
+ @ProfileType int profileType,
+ boolean isUserRunning) {
if (SafetySources.isDefaultEntryHidden(safetySource)) {
return null;
}
- boolean isQuietModeEnabled = isUserManaged && !isManagedUserRunning;
+ boolean isQuietModeEnabled = (profileType == PROFILE_TYPE_MANAGED) && !isUserRunning;
PendingIntent pendingIntent =
mPendingIntentFactory.getPendingIntent(
safetySource.getId(),
@@ -905,13 +894,7 @@ public final class SafetyCenterDataFactory {
return null;
}
- CharSequence title =
- isUserManaged
- ? DevicePolicyResources.getSafetySourceWorkString(
- mSafetyCenterResourcesApk,
- safetySource.getId(),
- safetySource.getTitleForWorkResId())
- : mSafetyCenterResourcesApk.getString(safetySource.getTitleResId());
+ CharSequence title = getTitleForProfileType(profileType, safetySource);
CharSequence summary =
mSafetyCenterDataManager.sourceHasError(
SafetySourceKey.of(safetySource.getId(), userId))
@@ -1242,6 +1225,29 @@ public final class SafetyCenterDataFactory {
return null;
}
+ private CharSequence getTitleForProfileType(
+ @ProfileType int profileType, SafetySource safetySource) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return mSafetyCenterResourcesApk.getString(safetySource.getTitleResId());
+ case PROFILE_TYPE_MANAGED:
+ return DevicePolicyResources.getSafetySourceWorkString(
+ mSafetyCenterResourcesApk,
+ safetySource.getId(),
+ safetySource.getTitleForWorkResId());
+ case PROFILE_TYPE_PRIVATE:
+ 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);
+ return mSafetyCenterResourcesApk.getString(safetySource.getTitleResId());
+ }
+ }
+
private static SafetySourceKey toSafetySourceKey(String safetyCenterEntryIdString) {
SafetyCenterEntryId id = SafetyCenterIds.entryIdFromString(safetyCenterEntryIdString);
return SafetySourceKey.of(id.getSafetySourceId(), id.getUserId());
diff --git a/service/java/com/android/safetycenter/SafetyCenterListeners.java b/service/java/com/android/safetycenter/SafetyCenterListeners.java
index 5f89f46ff..091daa0a6 100644
--- a/service/java/com/android/safetycenter/SafetyCenterListeners.java
+++ b/service/java/com/android/safetycenter/SafetyCenterListeners.java
@@ -87,7 +87,7 @@ final class SafetyCenterListeners {
*/
void deliverDataForUserProfileGroup(UserProfileGroup userProfileGroup) {
ArrayMap<String, SafetyCenterData> safetyCenterDataCache = new ArrayMap<>();
- int[] relevantUserIds = userProfileGroup.getProfileParentAndManagedRunningProfilesUserIds();
+ int[] relevantUserIds = userProfileGroup.getAllRunningProfilesUserIds();
for (int i = 0; i < relevantUserIds.length; i++) {
deliverUpdateForUser(
relevantUserIds[i],
@@ -105,7 +105,7 @@ final class SafetyCenterListeners {
void deliverErrorForUserProfileGroup(
UserProfileGroup userProfileGroup, SafetyCenterErrorDetails safetyCenterErrorDetails) {
ArrayMap<String, SafetyCenterData> safetyCenterDataCache = new ArrayMap<>();
- int[] relevantUserIds = userProfileGroup.getProfileParentAndManagedRunningProfilesUserIds();
+ int[] relevantUserIds = userProfileGroup.getAllRunningProfilesUserIds();
for (int i = 0; i < relevantUserIds.length; i++) {
deliverUpdateForUser(
relevantUserIds[i],
diff --git a/service/java/com/android/safetycenter/SafetyCenterRefreshTracker.java b/service/java/com/android/safetycenter/SafetyCenterRefreshTracker.java
index d98127300..cc23b4e02 100644
--- a/service/java/com/android/safetycenter/SafetyCenterRefreshTracker.java
+++ b/service/java/com/android/safetycenter/SafetyCenterRefreshTracker.java
@@ -34,7 +34,6 @@ import android.util.Log;
import androidx.annotation.Nullable;
-import com.android.permission.util.UserUtils;
import com.android.safetycenter.logging.SafetyCenterStatsdLogger;
import java.io.PrintWriter;
@@ -175,7 +174,7 @@ public final class SafetyCenterRefreshTracker {
SafetyCenterStatsdLogger.writeSourceRefreshSystemEvent(
requestType,
safetySourceKey.getSourceId(),
- UserUtils.isManagedProfile(safetySourceKey.getUserId(), mContext),
+ UserProfileGroup.getProfileTypeOfUser(safetySourceKey.getUserId(), mContext),
duration,
sourceResult,
refreshReason,
@@ -284,7 +283,7 @@ public final class SafetyCenterRefreshTracker {
SafetyCenterStatsdLogger.writeSourceRefreshSystemEvent(
requestType,
sourceKey.getSourceId(),
- UserUtils.isManagedProfile(sourceKey.getUserId(), mContext),
+ UserProfileGroup.getProfileTypeOfUser(sourceKey.getUserId(), mContext),
duration,
SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__TIMEOUT,
refreshReason,
diff --git a/service/java/com/android/safetycenter/SafetySources.java b/service/java/com/android/safetycenter/SafetySources.java
index 02d83d27b..7be0ef00e 100644
--- a/service/java/com/android/safetycenter/SafetySources.java
+++ b/service/java/com/android/safetycenter/SafetySources.java
@@ -20,6 +20,8 @@ import android.safetycenter.SafetySourceData;
import android.safetycenter.config.SafetySource;
import android.util.Log;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
+
/**
* A helper class to facilitate working with {@link SafetySource} objects.
*
@@ -48,6 +50,23 @@ public final class SafetySources {
/** Returns whether a {@link SafetySource} supports managed profiles. */
public static boolean supportsManagedProfiles(SafetySource safetySource) {
+ return supportsAllProfiles(safetySource);
+ }
+
+ /**
+ * Returns whether a {@link SafetySource} supports the profile of the given type
+ * {@code profileType}.
+ */
+ public static boolean supportsProfileType(
+ SafetySource safetySource, @ProfileType int profileType) {
+ if (UserProfileGroup.PROFILE_TYPE_PRIMARY == profileType) {
+ return true;
+ }
+ return supportsAllProfiles(safetySource);
+ }
+
+ /** Returns whether a {@link SafetySource} supports all profiles. */
+ private static boolean supportsAllProfiles(SafetySource safetySource) {
int safetySourceProfile = safetySource.getProfile();
switch (safetySourceProfile) {
case SafetySource.PROFILE_PRIMARY:
diff --git a/service/java/com/android/safetycenter/UserProfileGroup.java b/service/java/com/android/safetycenter/UserProfileGroup.java
index 74b9b136f..2f4ab17e4 100644
--- a/service/java/com/android/safetycenter/UserProfileGroup.java
+++ b/service/java/com/android/safetycenter/UserProfileGroup.java
@@ -18,6 +18,7 @@ package com.android.safetycenter;
import static java.util.Objects.requireNonNull;
+import android.annotation.IntDef;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -31,6 +32,8 @@ import androidx.annotation.Nullable;
import com.android.permission.util.UserUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -42,21 +45,47 @@ import java.util.Objects;
*
* @hide
*/
+//TODO(b/286539356) Do not expose the private profile when it's not running.
public final class UserProfileGroup {
private static final String TAG = "UserProfileGroup";
+ // UserHandle#USER_NULL is a @TestApi so it cannot be accessed from the mainline module.
+ public static final @UserIdInt int USER_NULL = -10000;
@UserIdInt private final int mProfileParentUserId;
private final int[] mManagedProfilesUserIds;
private final int[] mManagedRunningProfilesUserIds;
+ @UserIdInt private final int mPrivateProfileUserId;
+ private final boolean mPrivateProfileRunning;
+
+ /** Respresents the profile type of the primary user. */
+ public static final int PROFILE_TYPE_PRIMARY = 0;
+ /** Respresents the profile type of the managed profile. */
+ public static final int PROFILE_TYPE_MANAGED = 1;
+ /** Respresents the profile type of the private profile. */
+ public static final int PROFILE_TYPE_PRIVATE = 2;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {PROFILE_TYPE_PRIMARY, PROFILE_TYPE_MANAGED, PROFILE_TYPE_PRIVATE})
+ public @interface ProfileType {
+ // This array needs to cover all profile types. So whenever a new entry is added above then
+ // please remember to include it in this array as well.
+ int[] ALL_PROFILE_TYPES =
+ {PROFILE_TYPE_PRIMARY, PROFILE_TYPE_MANAGED, PROFILE_TYPE_PRIVATE};
+ }
+
private UserProfileGroup(
@UserIdInt int profileParentUserId,
int[] managedProfilesUserIds,
- int[] managedRunningProfilesUserIds) {
+ int[] managedRunningProfilesUserIds,
+ @UserIdInt int privateProfileUserId,
+ boolean privateProfileRunning) {
mProfileParentUserId = profileParentUserId;
mManagedProfilesUserIds = managedProfilesUserIds;
mManagedRunningProfilesUserIds = managedRunningProfilesUserIds;
+ mPrivateProfileUserId = privateProfileUserId;
+ mPrivateProfileRunning = privateProfileRunning;
}
/** Returns all the alive {@link UserProfileGroup}s. */
@@ -117,6 +146,10 @@ public final class UserProfileGroup {
int[] managedRunningProfilesUserIds = new int[userProfiles.size()];
int managedProfilesUserIdsLen = 0;
int managedRunningProfilesUserIdsLen = 0;
+
+ int privateProfileUserId = USER_NULL;
+ boolean privateProfileRunning = false;
+
for (int i = 0; i < userProfiles.size(); i++) {
UserHandle userProfileHandle = userProfiles.get(i);
int userProfileId = userProfileHandle.getIdentifier();
@@ -127,15 +160,19 @@ public final class UserProfileGroup {
managedRunningProfilesUserIds[managedRunningProfilesUserIdsLen++] =
userProfileId;
}
+ } else if (UserUtils.isPrivateProfile(userProfileId, context)) {
+ privateProfileUserId = userProfileId;
+ privateProfileRunning = UserUtils.isProfileRunning(userProfileId, context);
}
}
- UserProfileGroup userProfileGroup =
- new UserProfileGroup(
- profileParentUserId,
- Arrays.copyOf(managedProfilesUserIds, managedProfilesUserIdsLen),
- Arrays.copyOf(
- managedRunningProfilesUserIds, managedRunningProfilesUserIdsLen));
+ UserProfileGroup userProfileGroup = new UserProfileGroup(
+ profileParentUserId,
+ Arrays.copyOf(managedProfilesUserIds, managedProfilesUserIdsLen),
+ Arrays.copyOf(managedRunningProfilesUserIds, managedRunningProfilesUserIdsLen),
+ privateProfileUserId,
+ privateProfileRunning
+ );
if (!userProfileGroup.contains(userId)) {
Log.i(
TAG,
@@ -150,7 +187,8 @@ public final class UserProfileGroup {
if (!isProfile(userId, context)) {
return true;
}
- return UserUtils.isManagedProfile(userId, context);
+ return UserUtils.isManagedProfile(userId, context)
+ || UserUtils.isPrivateProfile(userId, context);
}
private static UserManager getUserManagerForUser(@UserIdInt int userId, Context context) {
@@ -208,32 +246,141 @@ public final class UserProfileGroup {
return mProfileParentUserId;
}
- /** Returns the managed profile user ids of the {@link UserProfileGroup}. */
- public int[] getManagedProfilesUserIds() {
- return mManagedProfilesUserIds;
- }
+ /**
+ * A convenience method to get all the profile ids of all the users of all profile types. So, in
+ * essence, this is equivalent to iterating through all the profile types using
+ * {@link ProfileType#ALL_PROFILE_TYPES} and getting all the users for each of the profile type
+ * using {@link #getProfilesOfType(int profileType)}
+ */
+ public int[] getAllProfilesUserIds() {
+ int[] allProfileIds = new int[getNumProfiles()];
+ allProfileIds[0] = mProfileParentUserId;
+ System.arraycopy(
+ mManagedProfilesUserIds,
+ /* srcPos= */ 0,
+ allProfileIds,
+ /* destPos= */ 1,
+ mManagedProfilesUserIds.length);
- /** Returns the running managed profile user ids of the {@link UserProfileGroup}. */
- public int[] getManagedRunningProfilesUserIds() {
- return mManagedRunningProfilesUserIds;
+ if (mPrivateProfileUserId != USER_NULL) {
+ allProfileIds[allProfileIds.length - 1] = mPrivateProfileUserId;
+ }
+
+ return allProfileIds;
}
/**
- * Convenience method that combines the results of {@link
- * UserProfileGroup#getProfileParentUserId()} and {@link
- * UserProfileGroup#getManagedRunningProfilesUserIds()}.
+ * A convenience method to get all the profile ids of all the users (that are currently running)
+ * of all profile types. So, in essence, this is equivalent to iterating through all the profile
+ * {types using {@link ProfileType#ALL_PROFILE_TYPES} and getting all the users for each of the
+ * profile type using {@link #getProfilesOfType(int profileType)} only if they are running.
*/
- public int[] getProfileParentAndManagedRunningProfilesUserIds() {
- int[] profileParentAndManagedRunningProfilesUserIds =
- new int[mManagedRunningProfilesUserIds.length + 1];
- profileParentAndManagedRunningProfilesUserIds[0] = mProfileParentUserId;
+ public int[] getAllRunningProfilesUserIds() {
+ int[] allRunningProfileIds = new int[getNumRunningProfiles()];
+ allRunningProfileIds[0] = mProfileParentUserId;
System.arraycopy(
mManagedRunningProfilesUserIds,
/* srcPos= */ 0,
- profileParentAndManagedRunningProfilesUserIds,
+ allRunningProfileIds,
/* destPos= */ 1,
mManagedRunningProfilesUserIds.length);
- return profileParentAndManagedRunningProfilesUserIds;
+
+ if (mPrivateProfileRunning) {
+ allRunningProfileIds[allRunningProfileIds.length - 1] = mPrivateProfileUserId;
+ }
+
+ return allRunningProfileIds;
+ }
+
+ /**
+ * Returns the profiles of the specified type. Returns an empty array if no profile of the
+ * specified type exists.
+ */
+ public int[] getProfilesOfType(@ProfileType int profileType) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return new int[] {mProfileParentUserId};
+ case PROFILE_TYPE_MANAGED:
+ return mManagedProfilesUserIds;
+ case PROFILE_TYPE_PRIVATE:
+ return mPrivateProfileUserId != USER_NULL
+ ? new int[]{mPrivateProfileUserId} : new int[]{};
+ default:
+ Log.w(TAG, "profiles requested for unexpected profile type " + profileType);
+ return new int[] {};
+ }
+ }
+
+ /**
+ * Returns the running profiles of the specified type. Returns an empty array if no profile of
+ * the specified type exists.
+ */
+ public int[] getRunningProfilesOfType(@ProfileType int profileType) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return new int[] {mProfileParentUserId};
+ case PROFILE_TYPE_MANAGED:
+ return mManagedRunningProfilesUserIds;
+ case PROFILE_TYPE_PRIVATE:
+ //TODO(b/286539356) add the new feature flag protection when available.
+ return mPrivateProfileRunning
+ ? new int[] {} : new int[] {mPrivateProfileUserId};
+ default:
+ Log.w(TAG, "Unexpected profile type " + profileType);
+ return new int[] {};
+ }
+ }
+
+ /** Returns the total number of running profiles in this user profile group */
+ public int getNumRunningProfiles() {
+ return 1
+ + mManagedRunningProfilesUserIds.length
+ + (mPrivateProfileRunning ? 1 : 0);
+ }
+
+ /** Returns the total number of profiles in this user profile group */
+ private int getNumProfiles() {
+ return 1
+ + mManagedProfilesUserIds.length
+ + (mPrivateProfileUserId == USER_NULL ? 0 : 1);
+ }
+
+ /**
+ * Returns the {@link ProfileType} for the provided {@code userId}. Note that the provided
+ * {@code userId} must be supported by the {@link UserProfileGroup} i.e.
+ * {@link #isSupported(int, Context)} should return true for {@code userId}.
+ */
+ public static @ProfileType int getProfileTypeOfUser(@UserIdInt int userId, Context context) {
+ if (UserUtils.isManagedProfile(userId, context)) {
+ return PROFILE_TYPE_MANAGED;
+ }
+ if (UserUtils.isPrivateProfile(userId, context)) {
+ return PROFILE_TYPE_PRIVATE;
+ }
+ return PROFILE_TYPE_PRIMARY;
+ }
+
+ /**
+ * Returns true iff the given userId is contained in this {@link UserProfileGroup} and it's
+ * running.
+ */
+ boolean containsRunningUserId(@UserIdInt int userId, @ProfileType int profileType) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return true;
+ case PROFILE_TYPE_MANAGED:
+ for (int i = 0; i < mManagedRunningProfilesUserIds.length; i++) {
+ if (mManagedRunningProfilesUserIds[i] == userId) {
+ return true;
+ }
+ }
+ return false;
+ case PROFILE_TYPE_PRIVATE:
+ return mPrivateProfileRunning;
+ default:
+ Log.w(TAG, "Unexpected profile type " + profileType);
+ return false;
+ }
}
/** Returns whether the {@link UserProfileGroup} contains the given {@code userId}. */
@@ -248,17 +395,7 @@ public final class UserProfileGroup {
}
}
- return false;
- }
-
- /** Returns whether the given {@code userId} is associated with a running managed profile. */
- boolean isManagedUserRunning(@UserIdInt int userId) {
- for (int i = 0; i < mManagedRunningProfilesUserIds.length; i++) {
- if (userId == mManagedRunningProfilesUserIds[i]) {
- return true;
- }
- }
- return false;
+ return USER_NULL != mPrivateProfileUserId && userId == mPrivateProfileUserId;
}
@Override
@@ -269,7 +406,9 @@ public final class UserProfileGroup {
return mProfileParentUserId == that.mProfileParentUserId
&& Arrays.equals(mManagedProfilesUserIds, that.mManagedProfilesUserIds)
&& Arrays.equals(
- mManagedRunningProfilesUserIds, that.mManagedRunningProfilesUserIds);
+ mManagedRunningProfilesUserIds, that.mManagedRunningProfilesUserIds)
+ && mPrivateProfileUserId == that.mPrivateProfileUserId
+ && mPrivateProfileRunning == that.mPrivateProfileRunning;
}
@Override
@@ -277,7 +416,9 @@ public final class UserProfileGroup {
return Objects.hash(
mProfileParentUserId,
Arrays.hashCode(mManagedProfilesUserIds),
- Arrays.hashCode(mManagedRunningProfilesUserIds));
+ Arrays.hashCode(mManagedRunningProfilesUserIds),
+ mPrivateProfileUserId,
+ mPrivateProfileRunning);
}
@Override
@@ -289,6 +430,10 @@ public final class UserProfileGroup {
+ Arrays.toString(mManagedProfilesUserIds)
+ ", mManagedRunningProfilesUserIds="
+ Arrays.toString(mManagedRunningProfilesUserIds)
+ + ", mPrivateProfileUserId"
+ + mPrivateProfileUserId
+ + ", mPrivateProfileRunning"
+ + mPrivateProfileRunning
+ '}';
}
}
diff --git a/service/java/com/android/safetycenter/data/SafetyCenterDataManager.java b/service/java/com/android/safetycenter/data/SafetyCenterDataManager.java
index dff7c4339..7385a2c3b 100644
--- a/service/java/com/android/safetycenter/data/SafetyCenterDataManager.java
+++ b/service/java/com/android/safetycenter/data/SafetyCenterDataManager.java
@@ -41,6 +41,7 @@ import com.android.safetycenter.SafetyCenterRefreshTracker;
import com.android.safetycenter.SafetySourceIssueInfo;
import com.android.safetycenter.SafetySourceKey;
import com.android.safetycenter.UserProfileGroup;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
import com.android.safetycenter.internaldata.SafetyCenterIssueActionId;
import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
import com.android.safetycenter.logging.SafetyCenterStatsdLogger;
@@ -567,7 +568,7 @@ public final class SafetyCenterDataManager {
* Writes a SafetySourceStateCollected atom for the given source in response to a stats pull.
*/
public void logSafetySourceStateCollectedAutomatic(
- SafetySourceKey sourceKey, boolean isManagedProfile) {
- mSafetySourceStateCollectedLogger.writeAutomaticAtom(sourceKey, isManagedProfile);
+ SafetySourceKey sourceKey, @ProfileType int profileType) {
+ mSafetySourceStateCollectedLogger.writeAutomaticAtom(sourceKey, profileType);
}
}
diff --git a/service/java/com/android/safetycenter/data/SafetyCenterInFlightIssueActionRepository.java b/service/java/com/android/safetycenter/data/SafetyCenterInFlightIssueActionRepository.java
index 82eb3a6c7..39809aa6f 100644
--- a/service/java/com/android/safetycenter/data/SafetyCenterInFlightIssueActionRepository.java
+++ b/service/java/com/android/safetycenter/data/SafetyCenterInFlightIssueActionRepository.java
@@ -28,8 +28,8 @@ import android.util.Log;
import androidx.annotation.Nullable;
-import com.android.permission.util.UserUtils;
import com.android.safetycenter.SafetySourceIssues;
+import com.android.safetycenter.UserProfileGroup;
import com.android.safetycenter.internaldata.SafetyCenterIssueActionId;
import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
import com.android.safetycenter.logging.SafetyCenterStatsdLogger;
@@ -87,7 +87,7 @@ final class SafetyCenterInFlightIssueActionRepository {
SafetyCenterStatsdLogger.writeInlineActionSystemEvent(
issueKey.getSafetySourceId(),
- UserUtils.isManagedProfile(issueKey.getUserId(), mContext),
+ UserProfileGroup.getProfileTypeOfUser(issueKey.getUserId(), mContext),
issueTypeId,
duration,
result);
diff --git a/service/java/com/android/safetycenter/data/SafetyCenterIssueRepository.java b/service/java/com/android/safetycenter/data/SafetyCenterIssueRepository.java
index 2e6f707a3..3806584a8 100644
--- a/service/java/com/android/safetycenter/data/SafetyCenterIssueRepository.java
+++ b/service/java/com/android/safetycenter/data/SafetyCenterIssueRepository.java
@@ -31,12 +31,12 @@ import android.safetycenter.config.SafetySourcesGroup;
import android.util.SparseArray;
import com.android.modules.utils.build.SdkLevel;
-import com.android.permission.util.UserUtils;
import com.android.safetycenter.SafetyCenterConfigReader;
import com.android.safetycenter.SafetySourceIssueInfo;
import com.android.safetycenter.SafetySourceKey;
import com.android.safetycenter.SafetySources;
import com.android.safetycenter.UserProfileGroup;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
import java.io.PrintWriter;
@@ -88,12 +88,12 @@ final class SafetyCenterIssueRepository {
* that can affect issues.
*/
void updateIssues(@UserIdInt int userId) {
- updateIssues(userId, UserUtils.isManagedProfile(userId, mContext));
+ updateIssues(userId, UserProfileGroup.getProfileTypeOfUser(userId, mContext));
}
- private void updateIssues(@UserIdInt int userId, boolean isManagedProfile) {
+ private void updateIssues(@UserIdInt int userId, @ProfileType int profileType) {
List<SafetySourceIssueInfo> issues =
- getAllStoredIssuesFromRawSourceData(userId, isManagedProfile);
+ getAllStoredIssuesFromRawSourceData(userId, profileType);
issues.sort(SAFETY_SOURCE_ISSUES_INFO_BY_SEVERITY_DESCENDING);
@@ -183,14 +183,14 @@ final class SafetyCenterIssueRepository {
}
private List<SafetySourceIssueInfo> getAllStoredIssuesFromRawSourceData(
- @UserIdInt int userId, boolean isManagedProfile) {
+ @UserIdInt int userId, @ProfileType int profileType) {
List<SafetySourceIssueInfo> allIssuesInfo = new ArrayList<>();
List<SafetySourcesGroup> safetySourcesGroups =
mSafetyCenterConfigReader.getSafetySourcesGroups();
for (int j = 0; j < safetySourcesGroups.size(); j++) {
addSafetySourceIssuesInfo(
- allIssuesInfo, safetySourcesGroups.get(j), userId, isManagedProfile);
+ allIssuesInfo, safetySourcesGroups.get(j), userId, profileType);
}
return allIssuesInfo;
@@ -200,7 +200,7 @@ final class SafetyCenterIssueRepository {
List<SafetySourceIssueInfo> issuesInfo,
SafetySourcesGroup safetySourcesGroup,
@UserIdInt int userId,
- boolean isManagedProfile) {
+ @ProfileType int profileType) {
List<SafetySource> safetySources = safetySourcesGroup.getSafetySources();
for (int i = 0; i < safetySources.size(); i++) {
SafetySource safetySource = safetySources.get(i);
@@ -208,7 +208,7 @@ final class SafetyCenterIssueRepository {
if (!SafetySources.isExternal(safetySource)) {
continue;
}
- if (isManagedProfile && !SafetySources.supportsManagedProfiles(safetySource)) {
+ if (!SafetySources.supportsProfileType(safetySource, profileType)) {
continue;
}
@@ -244,12 +244,11 @@ final class SafetyCenterIssueRepository {
* UserProfileGroup}.
*/
private List<SafetySourceIssueInfo> getIssuesFor(UserProfileGroup userProfileGroup) {
- List<SafetySourceIssueInfo> issues =
- new ArrayList<>(getIssuesForUser(userProfileGroup.getProfileParentUserId()));
+ List<SafetySourceIssueInfo> issues = new ArrayList<>();
- int[] managedRunningProfileUserIds = userProfileGroup.getManagedRunningProfilesUserIds();
- for (int i = 0; i < managedRunningProfileUserIds.length; i++) {
- issues.addAll(getIssuesForUser(managedRunningProfileUserIds[i]));
+ int[] allRunningProfileUserIds = userProfileGroup.getAllRunningProfilesUserIds();
+ for (int i = 0; i < allRunningProfileUserIds.length; i++) {
+ issues.addAll(getIssuesForUser(allRunningProfileUserIds[i]));
}
return issues;
diff --git a/service/java/com/android/safetycenter/data/SafetySourceDataValidator.java b/service/java/com/android/safetycenter/data/SafetySourceDataValidator.java
index 942e4ce18..4b74b0440 100644
--- a/service/java/com/android/safetycenter/data/SafetySourceDataValidator.java
+++ b/service/java/com/android/safetycenter/data/SafetySourceDataValidator.java
@@ -29,10 +29,10 @@ import android.util.Log;
import androidx.annotation.Nullable;
import com.android.modules.utils.build.SdkLevel;
-import com.android.permission.util.UserUtils;
import com.android.safetycenter.SafetyCenterConfigReader;
import com.android.safetycenter.SafetyCenterFlags;
import com.android.safetycenter.SafetySources;
+import com.android.safetycenter.UserProfileGroup;
import java.util.List;
import java.util.Set;
@@ -90,10 +90,14 @@ final class SafetySourceDataValidator {
validateCallingPackage(safetySource, packageName, safetySourceId);
}
- if (UserUtils.isManagedProfile(userId, mContext)
- && !SafetySources.supportsManagedProfiles(safetySource)) {
+ @UserProfileGroup.ProfileType int profileType =
+ UserProfileGroup.getProfileTypeOfUser(userId, mContext);
+ if (!SafetySources.supportsProfileType(safetySource, profileType)) {
throw new IllegalArgumentException(
- "Unexpected managed profile request for safety source: " + safetySourceId);
+ "Unexpected profile type: "
+ + profileType
+ + " for safety source: "
+ + safetySourceId);
}
boolean retrievingOrClearingData = safetySourceData == null;
diff --git a/service/java/com/android/safetycenter/data/SafetySourceStateCollectedLogger.java b/service/java/com/android/safetycenter/data/SafetySourceStateCollectedLogger.java
index e73459598..1bf8685bf 100644
--- a/service/java/com/android/safetycenter/data/SafetySourceStateCollectedLogger.java
+++ b/service/java/com/android/safetycenter/data/SafetySourceStateCollectedLogger.java
@@ -26,9 +26,10 @@ import android.safetycenter.SafetySourceStatus;
import androidx.annotation.Nullable;
-import com.android.permission.util.UserUtils;
import com.android.safetycenter.SafetySourceIssueInfo;
import com.android.safetycenter.SafetySourceKey;
+import com.android.safetycenter.UserProfileGroup;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
import com.android.safetycenter.logging.SafetyCenterStatsdLogger;
@@ -63,13 +64,13 @@ final class SafetySourceStateCollectedLogger {
/**
* Writes a SafetySourceStateCollected atom for the given source in response to a stats pull.
*/
- void writeAutomaticAtom(SafetySourceKey sourceKey, boolean isManagedProfile) {
+ void writeAutomaticAtom(SafetySourceKey sourceKey, @ProfileType int profileType) {
logSafetySourceStateCollected(
sourceKey,
mSourceDataRepository.getSafetySourceData(sourceKey),
/* refreshReason= */ null,
/* sourceDataDiffers= */ false,
- isManagedProfile,
+ profileType,
/* safetyEvent= */ null,
mSourceDataRepository.getSafetySourceLastUpdated(sourceKey));
}
@@ -90,7 +91,7 @@ final class SafetySourceStateCollectedLogger {
safetySourceData,
refreshReason,
sourceDataDiffers,
- UserUtils.isManagedProfile(userId, mContext),
+ UserProfileGroup.getProfileTypeOfUser(userId, mContext),
safetyEvent,
/* lastUpdatedElapsedTimeMillis= */ null);
}
@@ -100,7 +101,7 @@ final class SafetySourceStateCollectedLogger {
@Nullable SafetySourceData sourceData,
@Nullable @SafetyCenterManager.RefreshReason Integer refreshReason,
boolean sourceDataDiffers,
- boolean isManagedProfile,
+ @ProfileType int profileType,
@Nullable SafetyEvent safetyEvent,
@Nullable @ElapsedRealtimeLong Long lastUpdatedElapsedTimeMillis) {
SafetySourceStatus sourceStatus = sourceData == null ? null : sourceData.getStatus();
@@ -131,7 +132,7 @@ final class SafetySourceStateCollectedLogger {
Integer severityLevel = maxSeverityLevel > Integer.MIN_VALUE ? maxSeverityLevel : null;
SafetyCenterStatsdLogger.writeSafetySourceStateCollected(
sourceKey.getSourceId(),
- isManagedProfile,
+ profileType,
severityLevel,
openIssuesCount,
dismissedIssuesCount,
diff --git a/service/java/com/android/safetycenter/logging/SafetyCenterPullAtomCallback.java b/service/java/com/android/safetycenter/logging/SafetyCenterPullAtomCallback.java
index 168d73a0f..a8dc7568e 100644
--- a/service/java/com/android/safetycenter/logging/SafetyCenterPullAtomCallback.java
+++ b/service/java/com/android/safetycenter/logging/SafetyCenterPullAtomCallback.java
@@ -38,6 +38,7 @@ import com.android.safetycenter.SafetyCenterFlags;
import com.android.safetycenter.SafetySourceKey;
import com.android.safetycenter.SafetySources;
import com.android.safetycenter.UserProfileGroup;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
import com.android.safetycenter.data.SafetyCenterDataManager;
import java.util.List;
@@ -149,19 +150,19 @@ public final class SafetyCenterPullAtomCallback implements StatsPullAtomCallback
continue;
}
- writeSafetySourceStateCollectedAtomLocked(
- loggableSource,
- userProfileGroup.getProfileParentUserId(),
- /* isUserManaged= */ false);
-
- if (!SafetySources.supportsManagedProfiles(loggableSource)) {
- continue;
- }
-
- int[] managedIds = userProfileGroup.getManagedRunningProfilesUserIds();
- for (int k = 0; k < managedIds.length; k++) {
- writeSafetySourceStateCollectedAtomLocked(
- loggableSource, managedIds[k], /* isUserManaged= */ true);
+ for (int profileTypeIdx = 0;
+ profileTypeIdx < ProfileType.ALL_PROFILE_TYPES.length;
+ ++profileTypeIdx) {
+ @ProfileType int profileType = ProfileType.ALL_PROFILE_TYPES[profileTypeIdx];
+ if (!SafetySources.supportsProfileType(loggableSource, profileType)) {
+ continue;
+ }
+
+ int[] profileIds = userProfileGroup.getProfilesOfType(profileType);
+ for (int profileIdx = 0; profileIdx < profileIds.length; profileIdx++) {
+ writeSafetySourceStateCollectedAtomLocked(
+ loggableSource, profileIds[profileIdx], profileType);
+ }
}
}
}
@@ -169,8 +170,8 @@ public final class SafetyCenterPullAtomCallback implements StatsPullAtomCallback
@GuardedBy("mApiLock")
private void writeSafetySourceStateCollectedAtomLocked(
- SafetySource safetySource, @UserIdInt int userId, boolean isUserManaged) {
+ SafetySource safetySource, @UserIdInt int userId, @ProfileType int profileType) {
SafetySourceKey sourceKey = SafetySourceKey.of(safetySource.getId(), userId);
- mDataManager.logSafetySourceStateCollectedAutomatic(sourceKey, isUserManaged);
+ mDataManager.logSafetySourceStateCollectedAutomatic(sourceKey, profileType);
}
}
diff --git a/service/java/com/android/safetycenter/logging/SafetyCenterStatsdLogger.java b/service/java/com/android/safetycenter/logging/SafetyCenterStatsdLogger.java
index 710c3f7ac..3311d0c1f 100644
--- a/service/java/com/android/safetycenter/logging/SafetyCenterStatsdLogger.java
+++ b/service/java/com/android/safetycenter/logging/SafetyCenterStatsdLogger.java
@@ -25,6 +25,8 @@ import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTIO
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__NAVIGATION_SOURCE__SOURCE_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
+import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SENSOR__SENSOR_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SEVERITY_LEVEL__SAFETY_SEVERITY_CRITICAL_WARNING;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SEVERITY_LEVEL__SAFETY_SEVERITY_OK;
@@ -43,12 +45,15 @@ import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVE
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__TIMEOUT;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__COLLECTION_TYPE__AUTOMATIC;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__COLLECTION_TYPE__SOURCE_UPDATED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
+import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_CRITICAL_WARNING;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_LEVEL_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_OK;
@@ -69,6 +74,9 @@ import static com.android.permission.PermissionStatsLog.SAFETY_STATE__OVERALL_SE
import static com.android.permission.PermissionStatsLog.SAFETY_STATE__OVERALL_SEVERITY_LEVEL__SAFETY_SEVERITY_LEVEL_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_STATE__OVERALL_SEVERITY_LEVEL__SAFETY_SEVERITY_OK;
import static com.android.permission.PermissionStatsLog.SAFETY_STATE__OVERALL_SEVERITY_LEVEL__SAFETY_SEVERITY_RECOMMENDATION;
+import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_MANAGED;
+import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_PRIMARY;
+import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_PRIVATE;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
@@ -84,6 +92,7 @@ import androidx.annotation.Nullable;
import com.android.permission.PermissionStatsLog;
import com.android.safetycenter.SafetyCenterFlags;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -161,7 +170,7 @@ public final class SafetyCenterStatsdLogger {
/** Writes a {@link PermissionStatsLog#SAFETY_SOURCE_STATE_COLLECTED} atom. */
public static void writeSafetySourceStateCollected(
String sourceId,
- boolean isManagedProfile,
+ @ProfileType int profileType,
@Nullable @SafetySourceData.SeverityLevel Integer sourceSeverityLevel,
long openIssuesCount,
long dismissedIssuesCount,
@@ -181,7 +190,7 @@ public final class SafetyCenterStatsdLogger {
PermissionStatsLog.write(
SAFETY_SOURCE_STATE_COLLECTED,
idStringToLong(sourceId),
- toSourceStateCollectedProfileType(isManagedProfile),
+ toSourceStateCollectedProfileType(profileType),
toSafetySourceStateCollectedSeverityLevel(sourceSeverityLevel),
openIssuesCount,
dismissedIssuesCount,
@@ -203,7 +212,7 @@ public final class SafetyCenterStatsdLogger {
public static void writeSourceRefreshSystemEvent(
@RefreshRequestType int refreshType,
String sourceId,
- boolean isManagedProfile,
+ @ProfileType int profileType,
Duration duration,
@SystemEventResult int result,
long refreshReason,
@@ -215,7 +224,7 @@ public final class SafetyCenterStatsdLogger {
SAFETY_CENTER_SYSTEM_EVENT_REPORTED,
toSourceRefreshEventType(refreshType),
idStringToLong(sourceId),
- toSystemEventProfileType(isManagedProfile),
+ toSystemEventProfileType(profileType),
UNSET_ISSUE_TYPE_ID,
duration.toMillis(),
result,
@@ -254,7 +263,7 @@ public final class SafetyCenterStatsdLogger {
*/
public static void writeInlineActionSystemEvent(
String sourceId,
- boolean isManagedProfile,
+ @ProfileType int profileType,
@Nullable String issueTypeId,
Duration duration,
@SystemEventResult int result) {
@@ -265,7 +274,7 @@ public final class SafetyCenterStatsdLogger {
SAFETY_CENTER_SYSTEM_EVENT_REPORTED,
SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__INLINE_ACTION,
idStringToLong(sourceId),
- toSystemEventProfileType(isManagedProfile),
+ toSystemEventProfileType(profileType),
issueTypeId == null ? UNSET_ISSUE_TYPE_ID : idStringToLong(issueTypeId),
duration.toMillis(),
result,
@@ -279,13 +288,13 @@ public final class SafetyCenterStatsdLogger {
*/
public static void writeNotificationPostedEvent(
String sourceId,
- boolean isManagedProfile,
+ @ProfileType int profileType,
String issueTypeId,
@SafetySourceData.SeverityLevel int sourceSeverityLevel) {
writeNotificationInteractionReportedEvent(
SAFETY_CENTER_INTERACTION_REPORTED__ACTION__NOTIFICATION_POSTED,
sourceId,
- isManagedProfile,
+ profileType,
issueTypeId,
sourceSeverityLevel);
}
@@ -296,13 +305,13 @@ public final class SafetyCenterStatsdLogger {
*/
public static void writeNotificationDismissedEvent(
String sourceId,
- boolean isManagedProfile,
+ @ProfileType int profileType,
String issueTypeId,
@SafetySourceData.SeverityLevel int sourceSeverityLevel) {
writeNotificationInteractionReportedEvent(
SAFETY_CENTER_INTERACTION_REPORTED__ACTION__NOTIFICATION_DISMISSED,
sourceId,
- isManagedProfile,
+ profileType,
issueTypeId,
sourceSeverityLevel);
}
@@ -313,7 +322,7 @@ public final class SafetyCenterStatsdLogger {
*/
public static void writeNotificationActionClickedEvent(
String sourceId,
- boolean isManagedProfile,
+ @ProfileType int profileType,
String issueTypeId,
@SafetySourceData.SeverityLevel int sourceSeverityLevel,
boolean isPrimaryAction) {
@@ -322,13 +331,13 @@ public final class SafetyCenterStatsdLogger {
? SAFETY_CENTER_INTERACTION_REPORTED__ACTION__ISSUE_PRIMARY_ACTION_CLICKED
: SAFETY_CENTER_INTERACTION_REPORTED__ACTION__ISSUE_SECONDARY_ACTION_CLICKED;
writeNotificationInteractionReportedEvent(
- action, sourceId, isManagedProfile, issueTypeId, sourceSeverityLevel);
+ action, sourceId, profileType, issueTypeId, sourceSeverityLevel);
}
private static void writeNotificationInteractionReportedEvent(
int interactionReportedAction,
String sourceId,
- boolean isManagedProfile,
+ @ProfileType int profileType,
String issueTypeId,
@SafetySourceData.SeverityLevel int sourceSeverityLevel) {
if (!SafetyCenterFlags.getAllowStatsdLogging()) {
@@ -342,7 +351,7 @@ public final class SafetyCenterStatsdLogger {
SAFETY_CENTER_INTERACTION_REPORTED__NAVIGATION_SOURCE__SOURCE_UNKNOWN,
toInteractionReportedSeverityLevel(sourceSeverityLevel),
idStringToLong(sourceId),
- toInteractionReportedProfileType(isManagedProfile),
+ toInteractionReportedProfileType(profileType),
idStringToLong(issueTypeId),
SAFETY_CENTER_INTERACTION_REPORTED__SENSOR__SENSOR_UNKNOWN,
UNSET_SOURCE_GROUP_ID,
@@ -382,22 +391,43 @@ public final class SafetyCenterStatsdLogger {
return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__EVENT_TYPE_UNKNOWN;
}
- private static int toSourceStateCollectedProfileType(boolean isManagedProfile) {
- return isManagedProfile
- ? SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED
- : SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+ private static int toSourceStateCollectedProfileType(@ProfileType int profileType) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+ case PROFILE_TYPE_MANAGED:
+ return SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
+ case PROFILE_TYPE_PRIVATE:
+ return SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
+ }
+ Log.w(TAG, "state collect arg requested for unknown profile type " + profileType);
+ return SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
}
- private static int toSystemEventProfileType(boolean isManagedProfile) {
- return isManagedProfile
- ? SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED
- : SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+ private static int toSystemEventProfileType(@ProfileType int profileType) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+ case PROFILE_TYPE_MANAGED:
+ return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
+ case PROFILE_TYPE_PRIVATE:
+ return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
+ }
+ Log.w(TAG, "system event arg requested for unknown profile type " + profileType);
+ return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
}
- private static int toInteractionReportedProfileType(boolean isManagedProfile) {
- return isManagedProfile
- ? SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED
- : SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+ private static int toInteractionReportedProfileType(@ProfileType int profileType) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+ case PROFILE_TYPE_MANAGED:
+ return SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
+ case PROFILE_TYPE_PRIVATE:
+ return SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
+ }
+ Log.w(TAG, "interaction enum requested for unknown profile type " + profileType);
+ return SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
}
/**
diff --git a/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationReceiver.java b/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationReceiver.java
index ed0e95177..6cfa39580 100644
--- a/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationReceiver.java
+++ b/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationReceiver.java
@@ -27,7 +27,6 @@ import android.util.Log;
import androidx.annotation.Nullable;
import com.android.internal.annotations.GuardedBy;
-import com.android.permission.util.UserUtils;
import com.android.safetycenter.ApiLock;
import com.android.safetycenter.PendingIntentFactory;
import com.android.safetycenter.SafetyCenterDataChangeNotifier;
@@ -214,7 +213,7 @@ public final class SafetyCenterNotificationReceiver extends BroadcastReceiver {
if (dismissedIssue != null) {
SafetyCenterStatsdLogger.writeNotificationDismissedEvent(
issueKey.getSafetySourceId(),
- UserUtils.isManagedProfile(userId, context),
+ UserProfileGroup.getProfileTypeOfUser(userId, context),
dismissedIssue.getIssueTypeId(),
dismissedIssue.getSeverityLevel());
}
@@ -240,7 +239,7 @@ public final class SafetyCenterNotificationReceiver extends BroadcastReceiver {
if (issue != null) {
SafetyCenterStatsdLogger.writeNotificationActionClickedEvent(
issueKey.getSafetySourceId(),
- UserUtils.isManagedProfile(issueKey.getUserId(), context),
+ UserProfileGroup.getProfileTypeOfUser(issueKey.getUserId(), context),
issue.getIssueTypeId(),
issue.getSeverityLevel(),
SafetySourceIssues.isPrimaryAction(
diff --git a/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationSender.java b/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationSender.java
index d17090c34..2e298fa90 100644
--- a/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationSender.java
+++ b/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationSender.java
@@ -37,7 +37,6 @@ import android.util.Log;
import androidx.annotation.Nullable;
import com.android.modules.utils.build.SdkLevel;
-import com.android.permission.util.UserUtils;
import com.android.safetycenter.SafetyCenterFlags;
import com.android.safetycenter.SafetySourceIssueInfo;
import com.android.safetycenter.SafetySourceIssues;
@@ -210,11 +209,9 @@ public final class SafetyCenterNotificationSender {
/** Updates Safety Center notifications for the given {@link UserProfileGroup}. */
public void updateNotifications(UserProfileGroup userProfileGroup) {
- updateNotifications(userProfileGroup.getProfileParentUserId());
-
- int[] managedProfileUserIds = userProfileGroup.getManagedProfilesUserIds();
- for (int i = 0; i < managedProfileUserIds.length; i++) {
- updateNotifications(managedProfileUserIds[i]);
+ int[] allProfilesUserIds = userProfileGroup.getAllProfilesUserIds();
+ for (int i = 0; i < allProfilesUserIds.length; i++) {
+ updateNotifications(allProfilesUserIds[i]);
}
}
@@ -387,7 +384,7 @@ public final class SafetyCenterNotificationSender {
mNotifiedIssues.put(key, issue);
SafetyCenterStatsdLogger.writeNotificationPostedEvent(
key.getSafetySourceId(),
- UserUtils.isManagedProfile(key.getUserId(), mContext),
+ UserProfileGroup.getProfileTypeOfUser(key.getUserId(), mContext),
issue.getIssueTypeId(),
issue.getSeverityLevel());
}
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/Android.bp b/tests/functional/safetycenter/multiusers/Android.bp
index b5caf48e6..2f1cda9ed 100644
--- a/tests/functional/safetycenter/multiusers/Android.bp
+++ b/tests/functional/safetycenter/multiusers/Android.bp
@@ -36,6 +36,7 @@ android_test {
"Harrier",
"Nene",
"TestApp",
+ "permissions-aconfig-flags-lib",
],
test_suites: [
"general-tests",
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 acbc5cfc0..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
@@ -21,6 +21,8 @@ import android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
import android.app.PendingIntent
import android.content.Context
import android.os.UserHandle
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
import android.safetycenter.SafetyCenterData
import android.safetycenter.SafetyCenterEntry
import android.safetycenter.SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING
@@ -43,6 +45,7 @@ import com.android.bedstead.harrier.DeviceState
import com.android.bedstead.harrier.annotations.EnsureHasAdditionalUser
import com.android.bedstead.harrier.annotations.EnsureHasCloneProfile
import com.android.bedstead.harrier.annotations.EnsureHasNoWorkProfile
+import com.android.bedstead.harrier.annotations.EnsureHasPrivateProfile
import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile
import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner
import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDeviceOwner
@@ -227,6 +230,13 @@ class SafetyCenterMultiUsersTest {
.setEnabled(false)
.build()
+ private val dynamicDisabledForPrivateUpdated: SafetyCenterEntry
+ get() =
+ safetyCenterEntryOkForPrivate(DYNAMIC_DISABLED_ID, deviceState.privateProfile().id())
+
+ private val dynamicHiddenForPrivateUpdated: SafetyCenterEntry
+ get() = safetyCenterEntryOkForPrivate(DYNAMIC_HIDDEN_ID, deviceState.privateProfile().id())
+
private val staticGroupBuilder =
SafetyCenterEntryGroup.Builder(STATIC_GROUP_ID, "OK")
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
@@ -267,6 +277,24 @@ class SafetyCenterMultiUsersTest {
.setEnabled(false)
.build()
+ private val staticAllOptionalForPrivateBuilder
+ get() =
+ safetyCenterTestData
+ .safetyCenterEntryDefaultStaticBuilder(
+ STATIC_ALL_OPTIONAL_ID,
+ userId = deviceState.privateProfile().id(),
+ title = "Unknown"
+ )
+ .setPendingIntent(
+ createTestActivityRedirectPendingIntentForUser(
+ deviceState.privateProfile().userHandle(),
+ explicit = false
+ )
+ )
+
+ private val staticAllOptionalForPrivate
+ get() = staticAllOptionalForPrivateBuilder.build()
+
private fun createStaticEntry(explicit: Boolean = true): SafetyCenterStaticEntry =
SafetyCenterStaticEntry.Builder("OK")
.setSummary("OK")
@@ -292,9 +320,25 @@ class SafetyCenterMultiUsersTest {
)
)
+ private fun staticEntryForPrivateBuilder(
+ title: CharSequence = "Unknown",
+ explicit: Boolean = true
+ ) =
+ SafetyCenterStaticEntry.Builder(title)
+ .setSummary("OK")
+ .setPendingIntent(
+ createTestActivityRedirectPendingIntentForUser(
+ deviceState.privateProfile().userHandle(),
+ explicit
+ )
+ )
+
private fun createStaticEntryForWork(explicit: Boolean = true): SafetyCenterStaticEntry =
staticEntryForWorkBuilder(explicit = explicit).build()
+ private fun createStaticEntryForPrivate(explicit: Boolean = true): SafetyCenterStaticEntry =
+ staticEntryForPrivateBuilder(explicit = explicit).build()
+
private fun createStaticEntryForWorkPaused(): SafetyCenterStaticEntry =
staticEntryForWorkBuilder(explicit = false)
.setSummary(safetyCenterResourcesApk.getStringByName("work_profile_paused"))
@@ -313,6 +357,13 @@ class SafetyCenterMultiUsersTest {
.setPendingIntent(safetySourceTestData.createTestActivityRedirectPendingIntent())
.build()
+ private val staticEntryForPrivateUpdated: SafetyCenterStaticEntry
+ get() =
+ SafetyCenterStaticEntry.Builder("Unspecified title for Private")
+ .setSummary("Unspecified summary")
+ .setPendingIntent(safetySourceTestData.createTestActivityRedirectPendingIntent())
+ .build()
+
private val safetyCenterDataForAdditionalUser
get() =
SafetyCenterData(
@@ -657,10 +708,122 @@ class SafetyCenterMultiUsersTest {
@Test
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
- fun getSafetyCenterData_withComplexConfigWithAllDataProvided_returnsAllDataProvided() {
+ fun getSafetyCenterData_withComplexConfigWithExtraWorkOnlyWithAllDataProvided_returnsAllDataProvided() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig)
+ updatePrimaryProfileSources()
+ updateWorkProfileSources()
+
+ val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+
+ val managedUserId = deviceState.workProfile().id()
+ val safetyCenterDataFromComplexConfig =
+ SafetyCenterData(
+ safetyCenterTestData.safetyCenterStatusCritical(11),
+ listOf(
+ safetyCenterTestData.safetyCenterIssueCritical(
+ DYNAMIC_BAREBONE_ID,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueCritical(
+ ISSUE_ONLY_BAREBONE_ID,
+ attributionTitle = null,
+ groupId = ISSUE_ONLY_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueRecommendation(
+ DYNAMIC_DISABLED_ID,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueRecommendation(
+ ISSUE_ONLY_ALL_OPTIONAL_ID,
+ attributionTitle = null,
+ groupId = ISSUE_ONLY_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_IN_STATELESS_ID,
+ groupId = MIXED_STATELESS_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ ISSUE_ONLY_IN_STATELESS_ID,
+ groupId = MIXED_STATELESS_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_DISABLED_ID,
+ managedUserId,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_HIDDEN_ID,
+ managedUserId,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ ISSUE_ONLY_ALL_OPTIONAL_ID,
+ managedUserId,
+ attributionTitle = null,
+ groupId = ISSUE_ONLY_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_IN_STATELESS_ID,
+ managedUserId,
+ groupId = MIXED_STATELESS_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ ISSUE_ONLY_IN_STATELESS_ID,
+ managedUserId,
+ groupId = MIXED_STATELESS_GROUP_ID
+ )
+ ),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
+ .setSeverityLevel(ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .setSummary("Critical summary")
+ .setEntries(
+ listOf(
+ dynamicBareboneUpdated,
+ dynamicDisabledUpdated,
+ dynamicDisabledForWorkUpdated,
+ dynamicHiddenUpdated,
+ dynamicHiddenForWorkUpdated
+ )
+ )
+ .setSeverityUnspecifiedIconType(
+ SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION
+ )
+ .build()
+ ),
+ SafetyCenterEntryOrGroup(
+ staticGroupBuilder
+ .setEntries(
+ listOf(staticBarebone, staticAllOptional, staticAllOptionalForWork)
+ )
+ .build()
+ )
+ ),
+ listOf(
+ SafetyCenterStaticEntryGroup(
+ "OK",
+ listOf(
+ staticEntryUpdated,
+ staticEntryForWorkUpdated,
+ createStaticEntry(explicit = false),
+ createStaticEntryForWork(explicit = false)
+ )
+ )
+ )
+ )
+ assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig)
+ }
+
+ @Test
+ @RequiresFlagsDisabled(com.android.permission.flags.Flags.FLAG_PRIVATE_PROFILE_SUPPORTED)
+ @EnsureHasWorkProfile(installInstrumentedApp = TRUE)
+ @EnsureHasPrivateProfile(installInstrumentedApp = TRUE)
+ fun getSafetyCenterData_withComplexConfigWithPrivateProfileDisallowedWithAllDataProvided_returnsAllDataProvided() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig)
updatePrimaryProfileSources()
updateWorkProfileSources()
+ updatePrivateProfileSources()
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -764,6 +927,155 @@ class SafetyCenterMultiUsersTest {
assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig)
}
+ // TODO(b/286539356) add the os feature flag requirement when available.
+ @Test
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_PRIVATE_PROFILE_SUPPORTED)
+ @EnsureHasWorkProfile(installInstrumentedApp = TRUE)
+ @EnsureHasPrivateProfile(installInstrumentedApp = TRUE)
+ fun getSafetyCenterData_withComplexConfigWithAllDataProvided_returnsAllDataProvided() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig)
+ updatePrimaryProfileSources()
+ updateWorkProfileSources()
+ updatePrivateProfileSources()
+
+ val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+
+ val managedUserId = deviceState.workProfile().id()
+ val privateProfileId = deviceState.privateProfile().id()
+ val safetyCenterDataFromComplexConfig =
+ SafetyCenterData(
+ safetyCenterTestData.safetyCenterStatusCritical(11),
+ listOf(
+ safetyCenterTestData.safetyCenterIssueCritical(
+ DYNAMIC_BAREBONE_ID,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueCritical(
+ ISSUE_ONLY_BAREBONE_ID,
+ attributionTitle = null,
+ groupId = ISSUE_ONLY_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueRecommendation(
+ DYNAMIC_DISABLED_ID,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueRecommendation(
+ ISSUE_ONLY_ALL_OPTIONAL_ID,
+ attributionTitle = null,
+ groupId = ISSUE_ONLY_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_IN_STATELESS_ID,
+ groupId = MIXED_STATELESS_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ ISSUE_ONLY_IN_STATELESS_ID,
+ groupId = MIXED_STATELESS_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_DISABLED_ID,
+ managedUserId,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_HIDDEN_ID,
+ managedUserId,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ ISSUE_ONLY_ALL_OPTIONAL_ID,
+ managedUserId,
+ attributionTitle = null,
+ groupId = ISSUE_ONLY_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_IN_STATELESS_ID,
+ managedUserId,
+ groupId = MIXED_STATELESS_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ 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(
+ SafetyCenterEntryOrGroup(
+ SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
+ .setSeverityLevel(ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .setSummary("Critical summary")
+ .setEntries(
+ listOf(
+ dynamicBareboneUpdated,
+ dynamicDisabledUpdated,
+ dynamicDisabledForWorkUpdated,
+ dynamicDisabledForPrivateUpdated,
+ dynamicHiddenUpdated,
+ dynamicHiddenForWorkUpdated,
+ dynamicHiddenForPrivateUpdated
+ )
+ )
+ .setSeverityUnspecifiedIconType(
+ SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION
+ )
+ .build()
+ ),
+ SafetyCenterEntryOrGroup(
+ staticGroupBuilder
+ .setEntries(
+ listOf(
+ staticBarebone,
+ staticAllOptional,
+ staticAllOptionalForWork,
+ staticAllOptionalForPrivate
+ )
+ )
+ .build()
+ )
+ ),
+ listOf(
+ SafetyCenterStaticEntryGroup(
+ "OK",
+ listOf(
+ staticEntryUpdated,
+ staticEntryForWorkUpdated,
+ staticEntryForPrivateUpdated,
+ createStaticEntry(explicit = false),
+ createStaticEntryForWork(explicit = false),
+ createStaticEntryForPrivate(explicit = false)
+ )
+ )
+ )
+ )
+ assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig)
+ }
+
@Test
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
fun getSafetyCenterData_withQuietMode_shouldHaveWorkProfilePausedSummaryAndNoWorkIssues() {
@@ -920,6 +1232,79 @@ class SafetyCenterMultiUsersTest {
}
@Test
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_PRIVATE_PROFILE_SUPPORTED)
+ @EnsureHasPrivateProfile(installInstrumentedApp = TRUE)
+ fun getSafetyCenterData_afterPrivateProfileRemoved_returnsDefaultData() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
+ val privateSafetyCenterManager =
+ getSafetyCenterManagerForUser(deviceState.privateProfile().userHandle())
+ val safetyCenterDataWithPrivateProfile =
+ SafetyCenterData(
+ safetyCenterTestData.safetyCenterStatusUnknown,
+ emptyList(),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ SafetyCenterEntryGroup.Builder(SINGLE_SOURCE_GROUP_ID, "OK")
+ .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN)
+ .setSummary(
+ safetyCenterResourcesApk.getStringByName("group_unknown_summary")
+ )
+ .setEntries(
+ listOf(
+ safetyCenterTestData.safetyCenterEntryDefault(
+ SINGLE_SOURCE_ALL_PROFILE_ID
+ ),
+ safetyCenterTestData.safetyCenterEntryDefault(
+ SINGLE_SOURCE_ALL_PROFILE_ID,
+ deviceState.privateProfile().id(),
+ title = "Unknown",
+ pendingIntent =
+ createTestActivityRedirectPendingIntentForUser(
+ deviceState.privateProfile().userHandle()
+ )
+ )
+ )
+ )
+ .setSeverityUnspecifiedIconType(
+ SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION
+ )
+ .build()
+ )
+ ),
+ emptyList()
+ )
+
+ checkState(
+ safetyCenterManager.getSafetyCenterDataWithPermission() ==
+ safetyCenterDataWithPrivateProfile
+ )
+ checkState(
+ privateSafetyCenterManager.getSafetyCenterDataWithInteractAcrossUsersPermission() ==
+ safetyCenterDataWithPrivateProfile
+ )
+
+ deviceState.privateProfile().remove()
+
+ val safetyCenterDataForPrimaryUser =
+ SafetyCenterData(
+ safetyCenterTestData.safetyCenterStatusUnknown,
+ emptyList(),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ALL_PROFILE_ID)
+ )
+ ),
+ emptyList()
+ )
+ assertThat(safetyCenterManager.getSafetyCenterDataWithPermission())
+ .isEqualTo(safetyCenterDataForPrimaryUser)
+ assertThat(
+ privateSafetyCenterManager.getSafetyCenterDataWithInteractAcrossUsersPermission()
+ )
+ .isEqualTo(SafetyCenterTestData.DEFAULT)
+ }
+
+ @Test
@EnsureHasAdditionalUser(installInstrumentedApp = TRUE)
fun getSafetyCenterData_afterAdditionalUserRemoved_returnsDefaultData() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
@@ -1290,6 +1675,11 @@ class SafetyCenterMultiUsersTest {
.safetyCenterEntryOkBuilder(sourceId, managedUserId, title = "Ok title for Work")
.build()
+ private fun safetyCenterEntryOkForPrivate(sourceId: String, managedUserId: Int) =
+ safetyCenterTestData
+ .safetyCenterEntryOkBuilder(sourceId, managedUserId, title = "Ok title for Private")
+ .build()
+
private fun updatePrimaryProfileSources() {
safetyCenterTestHelper.setData(
DYNAMIC_BAREBONE_ID,
@@ -1342,4 +1732,29 @@ class SafetyCenterMultiUsersTest {
SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue)
)
}
+
+ private fun updatePrivateProfileSources() {
+ val privateSafetyCenterManager =
+ getSafetyCenterManagerForUser(deviceState.privateProfile().userHandle())
+ privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
+ DYNAMIC_DISABLED_ID,
+ safetySourceTestData.informationWithIssueForPrivate
+ )
+ privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
+ DYNAMIC_HIDDEN_ID,
+ safetySourceTestData.informationWithIssueForPrivate
+ )
+ privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
+ ISSUE_ONLY_ALL_OPTIONAL_ID,
+ SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue)
+ )
+ privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
+ DYNAMIC_IN_STATELESS_ID,
+ safetySourceTestData.unspecifiedWithIssueForPrivate
+ )
+ privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
+ ISSUE_ONLY_IN_STATELESS_ID,
+ SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue)
+ )
+ }
}
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) =
diff --git a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt
index 66be55fa8..559215c0c 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetySourceTestData.kt
@@ -175,6 +175,24 @@ class SafetySourceTestData(private val context: Context) {
.addIssue(informationIssue)
.build()
+ /**
+ * A [SafetySourceData] with a [SEVERITY_LEVEL_INFORMATION] redirecting [SafetySourceIssue] and
+ * a [SEVERITY_LEVEL_UNSPECIFIED] [SafetySourceStatus], to be used for a private profile entry.
+ */
+ val unspecifiedWithIssueForPrivate =
+ SafetySourceData.Builder()
+ .setStatus(
+ SafetySourceStatus.Builder(
+ "Unspecified title for Private",
+ "Unspecified summary",
+ SEVERITY_LEVEL_UNSPECIFIED
+ )
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
+ .build()
+ )
+ .addIssue(informationIssue)
+ .build()
+
/** A [SafetySourceData] with a [SEVERITY_LEVEL_INFORMATION] [SafetySourceStatus]. */
val information =
SafetySourceData.Builder()
@@ -284,6 +302,24 @@ class SafetySourceTestData(private val context: Context) {
/**
* A [SafetySourceData] with a [SEVERITY_LEVEL_INFORMATION] redirecting [SafetySourceIssue] and
+ * [SafetySourceStatus], to be used for a private profile entry.
+ */
+ val informationWithIssueForPrivate =
+ SafetySourceData.Builder()
+ .setStatus(
+ SafetySourceStatus.Builder(
+ "Ok title for Private",
+ "Ok summary",
+ SEVERITY_LEVEL_INFORMATION
+ )
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
+ .build()
+ )
+ .addIssue(informationIssue)
+ .build()
+
+ /**
+ * A [SafetySourceData] with a [SEVERITY_LEVEL_INFORMATION] redirecting [SafetySourceIssue] and
* [SafetySourceStatus].
*/
val informationWithSubtitleIssue =