[go: nahoru, domu]

Merge "Allow system supervisor to hold KILL_UID permission."
diff --git a/Android.bp b/Android.bp
index 74e9805..f9eee9d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -111,6 +111,7 @@
         package_prefixes: [
             "android.app.role",
             "android.safetycenter",
+            "android.safetylabel",
             "com.android.permission",
         ],
     },
diff --git a/PermissionController/Android.bp b/PermissionController/Android.bp
index 91c3447..cb2e287 100644
--- a/PermissionController/Android.bp
+++ b/PermissionController/Android.bp
@@ -78,7 +78,6 @@
     // artifact. See also b/209458854.
     sdk_version: "system_current",
     min_sdk_version: "30",
-    target_sdk_version: "33",
     updatable: true,
     privileged: true,
     certificate: "platform",
@@ -127,6 +126,7 @@
         "SettingsLibActionBarShadow",
         "SettingsLibProgressBar",
         "SettingsLibCollapsingToolbarBaseActivity",
+        "SettingsLibActivityEmbedding",
         "SettingsLibSettingsTheme",
         "SettingsLibFooterPreference",
         "SettingsLibSelectorWithWidgetPreference",
@@ -140,6 +140,7 @@
         "modules-utils-build_system",
         "safety-center-resources-lib",
         "lottie",
+        "safety-label"
     ],
 
     proto: {
@@ -155,8 +156,6 @@
         proguard_flags_files: ["proguard.flags"],
     },
 
-    plugins: ["java_api_finder"],
-
     kotlincflags: ["-Xjvm-default=enable"],
 
     apex_available: [
diff --git a/PermissionController/AndroidManifest.xml b/PermissionController/AndroidManifest.xml
index 814baf4..eda0d00 100644
--- a/PermissionController/AndroidManifest.xml
+++ b/PermissionController/AndroidManifest.xml
@@ -61,6 +61,7 @@
     <uses-permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
     <!--SYSTEM_APPLICATION_OVERLAY will be granted on T+, as installer protection is added in T -->
     <uses-permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY"/>
+    <uses-permission android:name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK" />
     <uses-permission android:name="android.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS"/>
     <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
     <uses-permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES" />
@@ -248,6 +249,17 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="com.android.permissioncontroller.permission.ui.v34.PermissionRationaleActivity"
+                  android:configChanges="keyboardHidden|screenSize"
+                  android:excludeFromRecents="true"
+                  android:exported="false"
+                  android:theme="@style/GrantPermissions.FilterTouches"
+                  android:visibleToInstantApps="true"
+                  android:inheritShowWhenLocked="true"
+                  android:hardwareAccelerated="false"
+                  android:canDisplayOnRemoteDevices="false">
+        </activity>
+
         <activity android:name="com.android.permissioncontroller.permission.ui.ManagePermissionsActivity"
                   android:configChanges="orientation|keyboardHidden|screenSize"
                   android:label="@string/app_permissions"
@@ -262,6 +274,7 @@
                 <action android:name="android.intent.action.REVIEW_PERMISSION_USAGE" />
                 <action android:name="android.intent.action.REVIEW_PERMISSION_HISTORY" />
                 <action android:name="android.intent.action.MANAGE_UNUSED_APPS" />
+                <action android:name="android.intent.action.REVIEW_APP_DATA_SHARING_UPDATES" />
                 <action android:name="android.permission.action.REVIEW_PERMISSION_DECISIONS"/>
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
@@ -312,7 +325,7 @@
         <activity android:name="com.android.permissioncontroller.permission.ui.OverlayWarningDialog"
                 android:excludeFromRecents="true"
                 android:exported="false"
-                android:theme="@style/Theme.AppCompat.DayNight.Dialog.Alert" />
+                android:theme="@style/Theme.DeviceDefault.Dialog.NoActionBar.DayNight" />
 
         <activity android:name="com.android.permissioncontroller.permission.ui.LocationProviderInterceptDialog"
                   android:excludeFromRecents="true"
@@ -523,9 +536,10 @@
         </receiver>
 
         <activity android:name="com.android.permissioncontroller.incident.ConfirmationActivity"
-                android:theme="@style/Theme.DeviceDefault.Dialog.Alert.DayNight"
+                android:theme="@style/Theme.DeviceDefault.Dialog.NoActionBar.DayNight"
                 android:exported="false"
                 android:excludeFromRecents="true"
+                android:finishOnCloseSystemDialogs="true"
                 android:noHistory="true" />
 
         <receiver android:name="com.android.permissioncontroller.incident.ApprovalReceiver"
diff --git a/PermissionController/iconloaderlib/build.gradle b/PermissionController/iconloaderlib/build.gradle
index 8410275..35ca8de 100644
--- a/PermissionController/iconloaderlib/build.gradle
+++ b/PermissionController/iconloaderlib/build.gradle
@@ -34,5 +34,5 @@
 }
 
 dependencies {
-    implementation "androidx.core:core:${ANDROID_X_VERSION}"
+    implementation "androidx.core:core"
 }
diff --git a/PermissionController/proguard.flags b/PermissionController/proguard.flags
index 1f0b032..13590aa 100644
--- a/PermissionController/proguard.flags
+++ b/PermissionController/proguard.flags
@@ -8,7 +8,12 @@
 -dontwarn androidx.core.**
 
 # Keep classes that implements RoleBehavior, which are used by reflection.
--keep class * implements com.android.permissioncontroller.role.model.RoleBehavior {
+-keep class * implements com.android.role.controller.model.RoleBehavior {
+    *;
+}
+
+# Keep classes that implements RoleUiBehavior, which are used by reflection.
+-keep class * implements com.android.permissioncontroller.role.ui.behavior.RoleUiBehavior {
     *;
 }
 
@@ -25,4 +30,4 @@
   *** get*();
   *** set*(***);
   *** has*();
-}
\ No newline at end of file
+}
diff --git a/PermissionController/res/drawable-v34/ic_business.xml b/PermissionController/res/drawable-v34/ic_business.xml
new file mode 100644
index 0000000..23ee37d
--- /dev/null
+++ b/PermissionController/res/drawable-v34/ic_business.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M12,7L12,3L2,3v18h20L22,7L12,7zM6,19L4,19v-2h2v2zM6,15L4,15v-2h2v2zM6,11L4,11L4,9h2v2zM6,7L4,7L4,5h2v2zM10,19L8,19v-2h2v2zM10,15L8,15v-2h2v2zM10,11L8,11L8,9h2v2zM10,7L8,7L8,5h2v2zM20,19h-8v-2h2v-2h-2v-2h2v-2h-2L12,9h8v10zM18,11h-2v2h2v-2zM18,15h-2v2h2v-2z"/>
+</vector>
diff --git a/PermissionController/res/drawable-v34/ic_collections_bookmark.xml b/PermissionController/res/drawable-v34/ic_collections_bookmark.xml
new file mode 100644
index 0000000..29f9e56
--- /dev/null
+++ b/PermissionController/res/drawable-v34/ic_collections_bookmark.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M4,6L2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6zM20,2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM20,16L8,16L8,4h5v7l2.5,-1.88L18,11L18,4h2v12z"/>
+</vector>
diff --git a/PermissionController/res/drawable-v34/ic_gear.xml b/PermissionController/res/drawable-v34/ic_gear.xml
new file mode 100644
index 0000000..958284d
--- /dev/null
+++ b/PermissionController/res/drawable-v34/ic_gear.xml
@@ -0,0 +1,13 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M13.85,22.25h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.53c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46 0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19c-0.59,-0.45 -0.74,-1.26 -0.37,-1.88l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73c0.26,-0.17 0.52,-0.32 0.78,-0.46l0.27,-1.91c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91c-0.1,0.68 -0.72,1.22 -1.46,1.22zM10.62,20.25h2.76l0.37,-2.55 0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34 2.38,0.96 1.38,-2.4 -2.03,-1.58 0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78s-0.03,-0.53 -0.06,-0.78l-0.07,-0.56 2.03,-1.58 -1.39,-2.4 -2.39,0.96 -0.45,-0.35c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77l-0.52,-0.22 -0.37,-2.55h-2.76l-0.37,2.55 -0.53,0.21c-0.44,0.19 -0.88,0.44 -1.34,0.79l-0.45,0.33 -2.38,-0.95 -1.39,2.39 2.03,1.58 -0.07,0.56c-0.03,0.26 -0.06,0.53 -0.06,0.79s0.02,0.53 0.06,0.78l0.07,0.56 -2.03,1.58 1.38,2.4 2.39,-0.96 0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22 0.38,2.55z"/>
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M12,12m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0"/>
+</vector>
diff --git a/PermissionController/res/drawable-v34/ic_info.xml b/PermissionController/res/drawable-v34/ic_info.xml
new file mode 100644
index 0000000..35f7f5f
--- /dev/null
+++ b/PermissionController/res/drawable-v34/ic_info.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/PermissionController/res/drawable/grant_dialog_permission_rationale_background.xml b/PermissionController/res/drawable/grant_dialog_permission_rationale_background.xml
new file mode 100644
index 0000000..b76b68c
--- /dev/null
+++ b/PermissionController/res/drawable/grant_dialog_permission_rationale_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+       android:shape="rectangle">
+    <corners android:radius="16dp"/>
+    <stroke android:width="1dp" android:color="?androidprv:attr/textColorSecondaryInverse" />
+</shape>
\ No newline at end of file
diff --git a/PermissionController/res/drawable/ic_more_info_arrow.xml b/PermissionController/res/drawable/ic_more_info_arrow.xml
new file mode 100644
index 0000000..73eb5cc
--- /dev/null
+++ b/PermissionController/res/drawable/ic_more_info_arrow.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp"
+        android:height="48dp" android:viewportWidth="48" android:viewportHeight="48"
+        android:autoMirrored="true" android:tint="?attr/colorControlNormal">
+    <path android:fillColor="@android:color/white"
+          android:pathData="M18.75,36 L16.6,33.85 26.5,23.95 16.6,14.05 18.75,11.9 30.8,23.95Z"/>
+</vector>
\ No newline at end of file
diff --git a/PermissionController/res/drawable/ic_shield_exclamation_outline.xml b/PermissionController/res/drawable/ic_shield_exclamation_outline.xml
new file mode 100644
index 0000000..5785bab
--- /dev/null
+++ b/PermissionController/res/drawable/ic_shield_exclamation_outline.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp"
+        android:height="48dp" android:viewportWidth="48" android:viewportHeight="48"
+        android:tint="?attr/colorControlNormal">
+    <path android:fillColor="@android:color/white"
+          android:pathData="M24,31.3Q24.7,31.3 25.2,30.8Q25.7,30.3 25.7,29.6Q25.7,28.9 25.2,28.4Q24.7,27.9 24,27.9Q23.3,27.9 22.8,28.4Q22.3,28.9 22.3,29.6Q22.3,30.3 22.8,30.8Q23.3,31.3 24,31.3ZM22.5,24.6H25.5V14.25H22.5ZM24,43.95Q17,42.2 12.5,35.825Q8,29.45 8,21.85V9.95L24,3.95L40,9.95V21.85Q40,29.45 35.5,35.825Q31,42.2 24,43.95ZM24,40.85Q29.75,38.95 33.375,33.675Q37,28.4 37,21.85V12.05L24,7.15L11,12.05V21.85Q11,28.4 14.625,33.675Q18.25,38.95 24,40.85ZM24,24Q24,24 24,24Q24,24 24,24Q24,24 24,24Q24,24 24,24Z"/>
+</vector>
diff --git a/PermissionController/res/layout-v33/preference_issue_card.xml b/PermissionController/res/layout-v33/preference_issue_card.xml
index 318ae01..36c04be 100644
--- a/PermissionController/res/layout-v33/preference_issue_card.xml
+++ b/PermissionController/res/layout-v33/preference_issue_card.xml
@@ -28,6 +28,12 @@
         style="@style/SafetyCenterIssueDismiss" />
 
     <TextView
+        android:id="@+id/issue_card_attribution_title"
+        android:text="@string/summary_placeholder"
+        android:importantForAccessibility="no"
+        style="@style/SafetyCenterIssueAttributionTitle" />
+
+    <TextView
         android:id="@+id/issue_card_title"
         android:text="@string/summary_placeholder"
         android:importantForAccessibility="no"
diff --git a/PermissionController/res/layout-v33/safety_center_qs.xml b/PermissionController/res/layout-v33/safety_center_qs.xml
index 664404e..1dbdf1c 100644
--- a/PermissionController/res/layout-v33/safety_center_qs.xml
+++ b/PermissionController/res/layout-v33/safety_center_qs.xml
@@ -31,53 +31,66 @@
             android:contentDescription="@string/safety_center_qs_close_button"
             style="@style/SafetyCenterQsCloseButton"/>
 
-        <FrameLayout
-            android:id="@+id/safety_center_prefs"
-            style="@style/SafetyCenterQsPreferences">
-        </FrameLayout>
-
-        <TextView
-            android:id="@+id/permission_section_title"
-            android:text="@string/sensor_permissions_qs"
-            android:visibility="gone"
-            android:focusable="true"
-            android:importantForAccessibility="yes"
-            style="@style/SafetyCenterQsSectionTitle"/>
-
         <LinearLayout
-            android:layout_width="match_parent"
+            android:id="@+id/main_page_contents"
             android:layout_height="wrap_content"
-            android:orientation="vertical"
-            android:id="@+id/permission_usage"
-            style="@style/SafetyCenterQsPermissionUsage"/>
+            style="@style/SafetyCenterQsBody">
 
-        <TextView
-            android:id="@+id/sensor_privacy_title"
-            android:text="@string/privacy_controls_qs"
-            android:focusable="true"
-            android:importantForAccessibility="yes"
-            style="@style/SafetyCenterQsSectionTitle"/>
+            <TextView
+                android:id="@+id/permission_section_title"
+                android:text="@string/sensor_permissions_qs"
+                android:visibility="gone"
+                android:focusable="true"
+                android:importantForAccessibility="yes"
+                style="@style/SafetyCenterQsSectionTitle"/>
 
-        <LinearLayout
-            android:background="@drawable/safety_entity_top_large_bottom_flat_background"
-            style="@style/SafetyCenterQsToggleContainer.Top">
-            <include android:id="@+id/camera_toggle"
-                     layout="@layout/safety_center_toggle_button"
-                     style="@style/SafetyCenterQsToggleButton.Start"/>
-            <include android:id="@+id/mic_toggle"
-                     layout="@layout/safety_center_toggle_button"
-                     style="@style/SafetyCenterQsToggleButton.End"/>
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:id="@+id/permission_usage"
+                style="@style/SafetyCenterQsPermissionUsage"/>
+
+            <TextView
+                android:id="@+id/status_section_title"
+                android:text="@string/safety_privacy_qs_tile_title"
+                android:visibility="gone"
+                android:focusable="true"
+                android:importantForAccessibility="yes"
+                style="@style/SafetyCenterQsSectionTitle"/>
+
+            <FrameLayout
+                android:id="@+id/safety_center_prefs"
+                style="@style/SafetyCenterQsPreferences">
+            </FrameLayout>
+
+            <TextView
+                android:id="@+id/sensor_privacy_title"
+                android:text="@string/privacy_controls_qs"
+                android:focusable="true"
+                android:importantForAccessibility="yes"
+                style="@style/SafetyCenterQsSectionTitle"/>
+
+            <LinearLayout
+                android:background="@drawable/safety_entity_top_large_bottom_flat_background"
+                style="@style/SafetyCenterQsToggleContainer.Top">
+                <include android:id="@+id/camera_toggle"
+                         layout="@layout/safety_center_toggle_button"
+                         style="@style/SafetyCenterQsToggleButton.Start"/>
+                <include android:id="@+id/mic_toggle"
+                         layout="@layout/safety_center_toggle_button"
+                         style="@style/SafetyCenterQsToggleButton.End"/>
+            </LinearLayout>
+            <LinearLayout
+                android:background="@drawable/safety_entity_top_flat_bottom_large_background"
+                style="@style/SafetyCenterQsToggleContainer.Bottom">
+                <include android:id="@+id/location_toggle"
+                         layout="@layout/safety_center_toggle_button"
+                         style="@style/SafetyCenterQsToggleButton.Start"/>
+                <include android:id="@+id/security_settings_button"
+                         layout="@layout/safety_center_toggle_button"
+                         style="@style/SafetyCenterQsToggleButton.End"/>
+            </LinearLayout>
         </LinearLayout>
-        <LinearLayout
-            android:background="@drawable/safety_entity_top_flat_bottom_large_background"
-            style="@style/SafetyCenterQsToggleContainer.Bottom">
-            <include android:id="@+id/location_toggle"
-                     layout="@layout/safety_center_toggle_button"
-                     style="@style/SafetyCenterQsToggleButton.Start"/>
-            <include android:id="@+id/security_settings_button"
-                     layout="@layout/safety_center_toggle_button"
-                     style="@style/SafetyCenterQsToggleButton.End"/>
-        </LinearLayout>
-
     </LinearLayout>
 </androidx.core.widget.NestedScrollView>
diff --git a/PermissionController/res/layout-v34/permission_rationale.xml b/PermissionController/res/layout-v34/permission_rationale.xml
new file mode 100644
index 0000000..c3b782d
--- /dev/null
+++ b/PermissionController/res/layout-v34/permission_rationale.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+
+<!--
+  ~ A lot of content in this file is identical to grant_permissions.xml
+  ~ Be sure to update both files when making changes.
+  -->
+
+<!-- In (hopefully very rare) case dialog is too high: allow scrolling -->
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    style="@style/PermissionRationaleScrollView">
+
+    <LinearLayout
+        android:id="@+id/grant_singleton"
+        android:importantForAccessibility="no"
+        android:focusable="false"
+        style="@style/PermissionRationaleSingleton">
+
+        <!-- The dialog -->
+        <LinearLayout
+            android:id="@+id/grant_dialog"
+            android:theme="@style/Theme.PermissionGrantDialog"
+            android:importantForAccessibility="no"
+            style="@style/PermissionRationaleDialog">
+
+            <LinearLayout
+                android:id="@+id/content_container"
+                style="@style/PermissionRationaleContent">
+
+                <LinearLayout
+                    style="@style/PermissionRationaleTitleContainer">
+
+                    <ImageView
+                        android:id="@+id/permission_icon"
+                        android:importantForAccessibility="no"
+                        android:src="@drawable/ic_shield_exclamation_outline"
+                        style="@style/PermissionRationaleTitleIcon" />
+
+                    <TextView
+                        android:id="@+id/permission_rationale_title"
+                        android:text="@string/permission_rationale_title"
+                        style="@style/PermissionRationaleTitleMessage" />
+
+                </LinearLayout>
+
+                <LinearLayout style="@style/PermissionRationaleSectionOuterContainer">
+                    <ImageView
+                        android:id="@+id/purpose_icon"
+                        android:importantForAccessibility="no"
+                        android:src="@drawable/ic_info"
+                        style="@style/PermissionRationaleSectionIcon" />
+                    <LinearLayout style="@style/PermissionRationaleSectionInnerContainer">
+                        <TextView
+                            android:id="@+id/purpose_title"
+                            android:text="@string/permission_rationale_purpose_title"
+                            style="@style/PermissionRationaleSectionTitle" />
+                        <TextView
+                            android:id="@+id/purpose_message"
+                            android:text="@string/permission_rationale_purpose_message"
+                            style="@style/PermissionRationaleSectionMessage" />
+                    </LinearLayout>
+                </LinearLayout>
+                <LinearLayout style="@style/PermissionRationaleSectionOuterContainer">
+                    <ImageView
+                        android:id="@+id/third_party_icon"
+                        android:importantForAccessibility="no"
+                        android:src="@drawable/ic_business"
+                        style="@style/PermissionRationaleSectionIcon" />
+                    <LinearLayout style="@style/PermissionRationaleSectionInnerContainer">
+                        <TextView
+                            android:id="@+id/third_party_title"
+                            android:text="@string/permission_rationale_thirdparty_title"
+                            style="@style/PermissionRationaleSectionTitle" />
+                        <TextView
+                            android:id="@+id/third_party_message"
+                            android:text="@string/permission_rationale_thirdparty_message"
+                            style="@style/PermissionRationaleSectionMessage" />
+                    </LinearLayout>
+                </LinearLayout>
+                <LinearLayout style="@style/PermissionRationaleSectionOuterContainer">
+                    <ImageView
+                        android:id="@+id/settings_icon"
+                        android:importantForAccessibility="no"
+                        android:src="@drawable/ic_gear"
+                        style="@style/PermissionRationaleSectionIcon" />
+                    <LinearLayout style="@style/PermissionRationaleSectionInnerContainer">
+                        <TextView
+                            android:id="@+id/settings_title"
+                            android:text="@string/permission_rationale_permission_settings_title"
+                            style="@style/PermissionRationaleSectionTitle" />
+                        <TextView
+                            android:id="@+id/settings_message"
+                            android:text="@string/permission_rationale_permission_settings_message"
+                            style="@style/PermissionRationaleSectionMessage" />
+                    </LinearLayout>
+                </LinearLayout>
+                <LinearLayout style="@style/PermissionRationaleSectionOuterContainer">
+                    <ImageView
+                        android:id="@+id/learn_more_icon"
+                        android:importantForAccessibility="no"
+                        android:src="@drawable/ic_collections_bookmark"
+                        style="@style/PermissionRationaleSectionIcon" />
+                    <LinearLayout style="@style/PermissionRationaleSectionInnerContainer">
+                        <TextView
+                            android:id="@+id/learn_more_message"
+                            android:text="@string/permission_rationale_permission_learn_more_title"
+                            style="@style/PermissionRationaleSectionTitle" />
+                    </LinearLayout>
+                </LinearLayout>
+
+                <LinearLayout style="@style/PermissionRationaleButtonContainer">
+                    <!-- TODO(b/260269197): update back button style -->
+                    <Button
+                        android:id="@+id/back_button"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:minHeight="36dp"
+                        android:layout_marginTop="6dp"
+                        android:layout_marginBottom="6dp"
+                        android:paddingTop="8dp"
+                        android:paddingBottom="8dp"
+                        android:paddingStart="16dp"
+                        android:paddingEnd="16dp"
+                        android:text="@string/back" />
+                </LinearLayout>
+
+            </LinearLayout>
+        </LinearLayout>
+    </LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/PermissionController/res/layout/app_permission.xml b/PermissionController/res/layout/app_permission.xml
index 697150b..3f92607 100644
--- a/PermissionController/res/layout/app_permission.xml
+++ b/PermissionController/res/layout/app_permission.xml
@@ -64,6 +64,11 @@
                         style="@style/AppPermissionRadioButton" />
 
                     <RadioButton
+                        android:id="@+id/select_photos_radio_button"
+                        android:text="@string/app_permission_button_select_photos"
+                        style="@style/AppPermissionRadioButton" />
+
+                    <RadioButton
                         android:id="@+id/ask_one_time_radio_button"
                         android:text="@string/app_permission_button_ask"
                         style="@style/AppPermissionRadioButton" />
diff --git a/PermissionController/res/layout/grant_permissions.xml b/PermissionController/res/layout/grant_permissions.xml
index 5940f7f..ca07061 100644
--- a/PermissionController/res/layout/grant_permissions.xml
+++ b/PermissionController/res/layout/grant_permissions.xml
@@ -62,6 +62,29 @@
 
             </LinearLayout>
 
+            <!-- permission rationale  -->
+            <LinearLayout
+                android:id="@+id/permission_rationale_container"
+                style="@style/PermissionGrantPermissionRationaleContent">
+
+                <ImageView
+                    android:id="@+id/permission_rationale_icon"
+                    android:importantForAccessibility="no"
+                    android:src="@drawable/ic_shield_exclamation_outline"
+                    style="@style/PermissionGrantPermissionRationaleIcon" />
+
+                <TextView
+                    android:id="@+id/permission_rationale_message"
+                    style="@style/PermissionGrantPermissionRationaleMessage" />
+
+                <ImageView
+                    android:id="@+id/permission_rationale_more_info_icon"
+                    android:importantForAccessibility="no"
+                    android:src="@drawable/ic_more_info_arrow"
+                    style="@style/PermissionGrantPermissionRationaleMoreInfoIcon" />
+
+            </LinearLayout>
+
             <!-- location (precise/approximate) animations -->
             <LinearLayout
                 android:layout_width="match_parent"
@@ -118,11 +141,31 @@
                     style="@style/PermissionGrantButtonAllowOneTime" />
 
                 <com.android.permissioncontroller.permission.ui.widget.SecureButton
+                    android:id="@+id/permission_allow_all_photos_button"
+                    android:text="@string/grant_dialog_button_allow_all_photos"
+                    style="@style/PermissionGrantButtonAllowAllPhotos" />
+
+                <com.android.permissioncontroller.permission.ui.widget.SecureButton
+                    android:id="@+id/permission_allow_more_selected_photos_button"
+                    android:text="@string/grant_dialog_button_allow_more_selected_photos"
+                    style="@style/PermissionGrantButtonAllowMorePhotos" />
+
+                <com.android.permissioncontroller.permission.ui.widget.SecureButton
+                    android:id="@+id/permission_allow_selected_photos_button"
+                    android:text="@string/grant_dialog_button_allow_selected_photos"
+                    style="@style/PermissionGrantButtonAllowSelectedPhotos" />
+
+                <com.android.permissioncontroller.permission.ui.widget.SecureButton
                     android:id="@+id/permission_deny_button"
                     android:text="@string/grant_dialog_button_deny"
                     style="@style/PermissionGrantButtonDeny" />
 
                 <com.android.permissioncontroller.permission.ui.widget.SecureButton
+                    android:id="@+id/permission_dont_allow_more_selected_photos_button"
+                    android:text="@string/grant_dialog_button_dont_allow_more_selected_photos"
+                    style="@style/PermissionGrantButtonDeny" />
+
+                <com.android.permissioncontroller.permission.ui.widget.SecureButton
                     android:id="@+id/permission_deny_and_dont_ask_again_button"
                     android:text="@string/grant_dialog_button_deny"
                     style="@style/PermissionGrantButtonDeny" />
diff --git a/PermissionController/res/layout/grant_permissions_material3.xml b/PermissionController/res/layout/grant_permissions_material3.xml
index a63f5d0..9c3ab6e 100644
--- a/PermissionController/res/layout/grant_permissions_material3.xml
+++ b/PermissionController/res/layout/grant_permissions_material3.xml
@@ -63,6 +63,29 @@
 
             </LinearLayout>
 
+            <!-- permission rationale  -->
+            <LinearLayout
+                android:id="@+id/permission_rationale_container"
+                style="@style/PermissionGrantPermissionRationaleContent">
+
+                <ImageView
+                    android:id="@+id/permission_rationale_icon"
+                    android:importantForAccessibility="no"
+                    android:src="@drawable/ic_shield_exclamation_outline"
+                    style="@style/PermissionGrantPermissionRationaleIcon" />
+
+                <TextView
+                    android:id="@+id/permission_rationale_message"
+                    style="@style/PermissionGrantPermissionRationaleMessage" />
+
+                <ImageView
+                    android:id="@+id/permission_rationale_more_info_icon"
+                    android:importantForAccessibility="no"
+                    android:src="@drawable/ic_more_info_arrow"
+                    style="@style/PermissionGrantPermissionRationaleMoreInfoIcon" />
+
+            </LinearLayout>
+
             <!-- location (precise/approximate) animations -->
             <LinearLayout
                 android:layout_width="match_parent"
@@ -71,7 +94,7 @@
 
                 <RadioGroup
                     android:id="@+id/permission_location_accuracy_radio_group"
-                    style="@style/PermissionLocationAccuracyRadioGroup">
+                    style="@style/PermissionLocationAccuracyRadioGroupMaterial3">
 
                     <RadioButton
                         android:id="@+id/permission_location_accuracy_radio_fine"
@@ -87,12 +110,12 @@
                 <ImageView
                     android:id="@+id/permission_location_accuracy_fine_only"
                     android:contentDescription="@string/precise_image_description"
-                    style="@style/PermissionLocationAccuracyFineImageView" />
+                    style="@style/PermissionLocationAccuracyFineImageViewMaterial3" />
 
                 <ImageView
                     android:id="@+id/permission_location_accuracy_coarse_only"
                     android:contentDescription="@string/approximate_image_description"
-                    style="@style/PermissionLocationAccuracyCoarseImageView" />
+                    style="@style/PermissionLocationAccuracyCoarseImageViewMaterial3" />
 
             </LinearLayout>
 
@@ -119,11 +142,31 @@
                     style="@style/PermissionGrantButtonAllowOneTimeMaterial3" />
 
                 <com.android.permissioncontroller.permission.ui.widget.SecureButton
+                    android:id="@+id/permission_allow_all_photos_button"
+                    android:text="@string/grant_dialog_button_allow_all_photos"
+                    style="@style/PermissionGrantButtonAllowAllPhotosMaterial3" />
+
+                <com.android.permissioncontroller.permission.ui.widget.SecureButton
+                    android:id="@+id/permission_allow_more_selected_photos_button"
+                    android:text="@string/grant_dialog_button_allow_more_selected_photos"
+                    style="@style/PermissionGrantButtonAllowMorePhotosMaterial3" />
+
+                <com.android.permissioncontroller.permission.ui.widget.SecureButton
+                    android:id="@+id/permission_allow_selected_photos_button"
+                    android:text="@string/grant_dialog_button_allow_selected_photos"
+                    style="@style/PermissionGrantButtonAllowSelectedPhotosMaterial3" />
+
+                <com.android.permissioncontroller.permission.ui.widget.SecureButton
                     android:id="@+id/permission_deny_button"
                     android:text="@string/grant_dialog_button_deny"
                     style="@style/PermissionGrantButtonDenyMaterial3" />
 
                 <com.android.permissioncontroller.permission.ui.widget.SecureButton
+                    android:id="@+id/permission_dont_allow_more_selected_photos_button"
+                    android:text="@string/grant_dialog_button_dont_allow_more_selected_photos"
+                    style="@style/PermissionGrantButtonDontAllowMorePhotosMaterial3" />
+
+                <com.android.permissioncontroller.permission.ui.widget.SecureButton
                     android:id="@+id/permission_deny_and_dont_ask_again_button"
                     android:text="@string/grant_dialog_button_deny"
                     style="@style/PermissionGrantButtonDenyMaterial3" />
diff --git a/PermissionController/res/navigation/nav_graph.xml b/PermissionController/res/navigation/nav_graph.xml
index cd3ba59..0ba46c5 100644
--- a/PermissionController/res/navigation/nav_graph.xml
+++ b/PermissionController/res/navigation/nav_graph.xml
@@ -186,4 +186,9 @@
             app:popExitAnim="@anim/activity_close_exit"
             app:popEnterAnim="@anim/activity_open_enter"/>
     </fragment>
+
+    <fragment
+        android:id="@+id/app_data_sharing_updates"
+        android:name="com.android.permissioncontroller.permission.ui.handheld.v34.AppDataSharingUpdatesFragment"
+        android:label="AppDataSharingUpdates"/>
 </navigation>
\ No newline at end of file
diff --git a/PermissionController/res/values-af/strings.xml b/PermissionController/res/values-af/strings.xml
index fc436d0..ff64419 100644
--- a/PermissionController/res/values-af/strings.xml
+++ b/PermissionController/res/values-af/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Hou \"Terwyl die program gebruik word\""</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Hou \"Net hierdie keer\""</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Meer inligting"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Gee toegang tot alle foto’s"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Kies foto’s"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Kies meer foto’s"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Moenie meer foto’s kies nie"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Moet steeds nie toelaat nie"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Maak toe"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> van <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Laat net toegang tot media toe"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Laat altyd toe"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Laat net toe terwyl jy program gebruik"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Laat alle foto’s toe"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Laat geselekteerde foto’s toe"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Vra elke keer"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Moenie toelaat nie"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Presiese ligging"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot &lt;b&gt;foto\'s, video\'s, musiek, oudio en ander lêers&lt;/b&gt; op hierdie toestel?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot musiek en oudio op hierdie toestel?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot foto\'s en video\'s op hierdie toestel?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot meer foto’s?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om oudio op te neem?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Die program sal net kan oudio opneem terwyl jy die program gebruik"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om oudio op te neem?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Wys ’n boodskap wanneer programme toegang het tot teks, prente of ander inhoud wat jy gekopieer het"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Wys wagwoorde"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Wys karakters kortliks terwyl jy tik"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Hierdie app het verklaar dat dit dalk <xliff:g id="PERMISSION_NAME">%s</xliff:g>-data met derde party sal deel"</string>
 </resources>
diff --git a/PermissionController/res/values-am/strings.xml b/PermissionController/res/values-am/strings.xml
index 0250bdf..88dc043 100644
--- a/PermissionController/res/values-am/strings.xml
+++ b/PermissionController/res/values-am/strings.xml
@@ -32,6 +32,14 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"«መተግበሪያው በጥቅም ላይ እያለ» አቆይ"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"«አሁን ብቻ»ን አቆይ"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"ተጨማሪ መረጃ"</string>
+    <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) -->
+    <skip />
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"ለማንኛውም አትፍቀድ"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"አሰናብት"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> ከ<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +194,10 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"ለሚዲያ ብቻ መዳረሻ ይፍቀዱ"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ሁልጊዜ ፍቀድ"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"መተግበሪያዎን እየተጠቀሙ እያሉ ብቻ ይፍቀዱ"</string>
+    <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) -->
+    <skip />
+    <!-- no translation found for app_permission_button_select_photos (1022930616634145364) -->
+    <skip />
     <string name="app_permission_button_ask" msgid="3342950658789427">"ሁልጊዜ ጠይቅ"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"አትፍቀድ"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"ትክክለኛ አካባቢ"</string>
@@ -468,6 +480,8 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; በመሣሪያዎ ላይ ያሉ &lt;b&gt;ፎቶዎችን፣ ቪዲዮዎችን፣ ሙዚቃን፣ ኦዲዮን፣ ቪዲዮዎችን እና ሌሎች ፋይሎችን&lt;/b&gt; ዘንድ እንዲደርስ ይፈቀድለት?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; በዚህ መሣሪያ ላይ ያለ ሙዚቃን እና ሌሎች የኦዲዮ ፋይሎችን እንዲደርስ ይፈቀድለት?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; በዚህ መሣሪያ ላይ ያሉ ፎቶዎችን እና ቪዲዮዎችን እንዲደርስ ይፈቀድለት?"</string>
+    <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) -->
+    <skip />
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ኦዲዮን እንዲቀዳ ይፈቀድለት?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"መተግበሪያው ኦዲዮን መቅዳት የሚችለው መተግበሪያውን እርስዎ ሲጠቀሙበት ብቻ ነው"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ኦዲዮን እንዲቀዳ ይፈቀድለት?"</string>
@@ -578,4 +592,6 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"መተግበሪያዎች ጽሑፍን፣ ምስሎችን ወይም እርስዎ የቀዱትን ሌላ ይዘት ሲደርሱ መልዕክት አሳይ"</string>
     <string name="show_password_title" msgid="2877269286984684659">"የይለፍ ቃላትን አሳይ"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"እርስዎ በሚተይቡበት ጊዜ ቁምፊዎችን በአጭሩ ያሳይ"</string>
+    <!-- no translation found for permission_rationale_message_template (4497650516269082051) -->
+    <skip />
 </resources>
diff --git a/PermissionController/res/values-ar/strings.xml b/PermissionController/res/values-ar/strings.xml
index 07c1433..89918c3 100644
--- a/PermissionController/res/values-ar/strings.xml
+++ b/PermissionController/res/values-ar/strings.xml
@@ -32,6 +32,14 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"عدم تغيير الإذن \"السماح فقط أثناء استخدام التطبيق\""</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"مواصلة استخدام الإذن \"هذه المرة فقط\""</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"معلومات أكثر"</string>
+    <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) -->
+    <skip />
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"عدم السماح على أي حال"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"رفض"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> من <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -48,7 +56,7 @@
     <string name="grant_dialog_button_allow_foreground" msgid="501896824973636533">"أثناء استخدام التطبيق"</string>
     <string name="grant_dialog_button_change_to_precise_location" msgid="3273115879467236033">"تغيير إلى الموقع الجغرافي الدقيق"</string>
     <string name="grant_dialog_button_keey_approximate_location" msgid="438025182769080011">"البقاء على الموقع الجغرافي التقريبي"</string>
-    <string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"هذه المرَّة فقط"</string>
+    <string name="grant_dialog_button_allow_one_time" msgid="2618088516449706391">"هذه المرّة فقط"</string>
     <string name="grant_dialog_button_allow_background" msgid="8236044729434367833">"السماح طوال الوقت"</string>
     <string name="grant_dialog_button_allow_all_files" msgid="4955436994954829894">"السماح بإدارة كل الملفات"</string>
     <string name="grant_dialog_button_allow_media_only" msgid="4832877658422573832">"السماح بالوصول إلى ملفات الوسائط"</string>
@@ -186,6 +194,10 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"السماح بالوصول إلى الوسائط فقط"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"السماح طوال الوقت"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"السماح عند استخدام التطبيق فقط"</string>
+    <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) -->
+    <skip />
+    <!-- no translation found for app_permission_button_select_photos (1022930616634145364) -->
+    <skip />
     <string name="app_permission_button_ask" msgid="3342950658789427">"الطلب في كل مرة"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"عدم السماح"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"الموقع الجغرافي الدقيق"</string>
@@ -468,6 +480,8 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"‏هل تسمح بوصول &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; إلى &lt;b&gt;الصور والفيديوهات والموسيقى والملفات الصوتية وملفات أخرى&lt;/b&gt; على هذا الجهاز؟"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"‏هل تريد السماح بوصول &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; إلى المقاطع الموسيقية والملفات الصوتية على هذا الجهاز؟"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"‏هل تريد السماح بوصول &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; إلى الصور والفيديوهات على هذا الجهاز؟"</string>
+    <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) -->
+    <skip />
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بتسجيل الصوت؟"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"لن يتمكن هذا التطبيق من تسجيل الصوت إلا عندما يكون قيد الاستخدام"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"‏هل تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بتسجيل الصوت؟"</string>
@@ -578,4 +592,6 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"عرض رسالة عندما يصل التطبيق إلى نص أو صور أو محتوى آخر تم نسخه."</string>
     <string name="show_password_title" msgid="2877269286984684659">"عرض كلمات المرور"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"عرض الأحرف لفترة وجيزة أثناء الكتابة"</string>
+    <!-- no translation found for permission_rationale_message_template (4497650516269082051) -->
+    <skip />
 </resources>
diff --git a/PermissionController/res/values-as/strings.xml b/PermissionController/res/values-as/strings.xml
index 6bb5e80..14b1b8d 100644
--- a/PermissionController/res/values-as/strings.xml
+++ b/PermissionController/res/values-as/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“এপ্‌টো ব্যৱহাৰ হৈ থকা অৱস্থাত” ৰাখক"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“কেৱল এইবাৰৰ বাবে অনুমতি দিয়ক” বিকল্পটো ৰাখক"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"অধিক তথ্য"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"আটাইবোৰ ফট’ এক্সেছৰ অনুমতি দিয়ক"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ফট’ বাছনি কৰক"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"অধিক ফট’ বাছক"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"অধিক ফট’ বাছনি নকৰিব"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"তথাপি অনুমতি নিদিব"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"অগ্ৰাহ্য কৰক"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>ৰ ভিতৰত <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>টা"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"কেৱল মিডিয়ালৈ এক্সেছৰ অনুমতি দিয়ক"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"সকলো সময়ৰ বাবে অনুমতি দিয়ক"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"কেৱল এপ্ ব্যৱহাৰ হৈ থাকোঁতে অনুমতি দিয়ক"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"আটাইবোৰ ফট’ৰ অনুমতি দিয়ক"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"বাছনি কৰা ফট’ৰ অনুমতি দিয়ক"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"প্ৰতিবাৰতে সোধক"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"অনুমতি নিদিব"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"সঠিক অৱস্থান"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক এই ডিভাইচটোত থকা &lt;b&gt;ফট’, ভিডিঅ’, সংগীত, অডিঅ’ আৰু অন্য ফাইল&lt;/b&gt; এক্সেছ কৰিবলৈ দিবনে?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক এই ডিভাইচটোত থকা সংগীত আৰু অডিঅ’ এক্সেছ কৰিবলৈ অনুমতি দিবনে?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক এই ডিভাইচটোত থকা ফট’ আৰু ভিডিঅ’ এক্সেছ কৰিবলৈ অনুমতি দিবনে?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক অধিক ফট’ এক্সেছ কৰাৰ অনুমতি দিবনে?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক অডিঅ\' ৰেকৰ্ড কৰিবলৈ অনুমতি দিবনে?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"আপুনি এই এপ্‌টো ব্যৱহাৰ কৰি থকাৰ সময়তহে কেৱল ই অডিঅ’ ৰেকৰ্ড কৰিব পাৰিব"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক অডিঅ’ ৰেকৰ্ড কৰিবলৈ অনুমতি দিবনে?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"এপে আপুনি প্ৰতিলিপি কৰা পাঠ, প্ৰতিচ্ছবি অথবা অন্য সমল এক্সেছ কৰিলে এটা বাৰ্তা দেখুৱাওক"</string>
     <string name="show_password_title" msgid="2877269286984684659">"পাছৱৰ্ডবোৰ দেখুৱাওক"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"আপুনি টাইপ কৰাৰ লগে লগে বৰ্ণসমূহ খন্তেকৰ বাবে দেখুৱাওক"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"এই এপ্‌টোৱে তৃতীয় পক্ষৰ সৈতে <xliff:g id="PERMISSION_NAME">%s</xliff:g>ৰ ডেটা শ্বেয়াৰ কৰিব পাৰে বুলি জনাইছে"</string>
 </resources>
diff --git a/PermissionController/res/values-az/strings.xml b/PermissionController/res/values-az/strings.xml
index bbe6f6b..a63a5ea 100644
--- a/PermissionController/res/values-az/strings.xml
+++ b/PermissionController/res/values-az/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“Tətbiq istifadə edilən zaman” saxlansın"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“Ancaq bu dəfə” saxlanılsın"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Ətraflı məlumat"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Bütün fotolara girişə icazə verin"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Fotolar seçin"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Daha çox foto seçin"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Daha çox foto seçməyin"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"İstənilən halda icazə verməyin"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"İmtina edin"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Yalnız mediaya giriş icazəsi verin"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Həmişə icazə verin"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Yalnız istifadə zamanı"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Bütün fotolara icazə verin"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Seçilmiş fotolara icazə verin"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Həmişə soruşulsun"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"İcazə verməyin"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Dəqiq məkan"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinin bu cihazdakı &lt;b&gt;foto, video, musiqi, audio və digər fayllara&lt;/b&gt; girişinə icazə verilsin?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə bu cihazdakı musiqi və audioya girişinə icazə verilsin?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinin cihazdakı foto və videolara girişinə icazə verilsin?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinin daha çox fotoya girişinə icazə verilsin?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə səs yazmaq icazəsi verilsin?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Tətbiq yalnız ondan istifadə etiyiniz zaman audio yaza biləcək"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə audio yazmaq icazəsi verilsin?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Tətbiq kopyalanmış mətn, şəkil və ya digər kontent işlədəndə bildiriş göstərilsin"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Parolları göstərin"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Yazarkən simvollar qısa müddət göstərilsin"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Bu tətbiq <xliff:g id="PERMISSION_NAME">%s</xliff:g> datasını üçüncü tərəflərlə paylaşa biləcəyini bildirdi"</string>
 </resources>
diff --git a/PermissionController/res/values-b+sr+Latn/strings.xml b/PermissionController/res/values-b+sr+Latn/strings.xml
index 42a042e..fcd778b 100644
--- a/PermissionController/res/values-b+sr+Latn/strings.xml
+++ b/PermissionController/res/values-b+sr+Latn/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Zadrži „Dok se aplikacija koristi“"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Zadrži Samo ovaj put"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Više informacija"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Dozvoli pristup svim slikama"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Izaberite slike"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Izaberite još slika"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Nemojte da birate više slika"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ionako ne dozvoli"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Odbaci"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> od <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Dozvoli samo pristup medijima"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Dozvoli uvek"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Dozv. samo dok se apl. koristi"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Dozvoli sve slike"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Dozvoli izabrane slike"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Pitaj svaki put"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Ne dozvoli"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Precizna lokacija"</string>
@@ -348,7 +354,7 @@
     <string name="role_assistant_short_label" msgid="3369003713187703399">"Aplikacija digitalnog pomoćnika"</string>
     <string name="role_assistant_description" msgid="6622458130459922952">"Aplikacije za pomoć mogu da vam pomognu na osnovu informacija sa ekrana koji gledate. Neke aplikacije podržavaju usluge pokretača i glasovnog unosa da bi vam pružile integrisanu pomoć."</string>
     <string name="role_browser_label" msgid="2877796144554070207">"Podrazumevana apl. pregledača"</string>
-    <string name="role_browser_short_label" msgid="6745009127123292296">"Aplikacija za pregledač"</string>
+    <string name="role_browser_short_label" msgid="6745009127123292296">"Aplikacija pregledača"</string>
     <string name="role_browser_description" msgid="3465253637499842671">"Aplikacije koje vam daju pristup internetu i prikazuju linkove koje možete da dodirnete"</string>
     <string name="role_browser_request_title" msgid="2895200507835937192">"Želite li da podesite <xliff:g id="APP_NAME">%1$s</xliff:g> kao podrazumevanu aplikaciju za pregledanje?"</string>
     <string name="role_browser_request_description" msgid="5888803407905985941">"Nije potrebna nijedna dozvola"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Pristup slikama, videu, muzici, zvuku i drugom na uređaju za &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Dozvoljavate li pristup muzici i zvuku na ovom uređaju za &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Dozvoljavate li pristup slikama i videu na ovom uređaju za &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Želite li da aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dozvolite pristup većem broju slika?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snima zvuk?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacija će moći da snima zvuk samo dok koristite aplikaciju"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snima zvuk?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Prikazuje poruku kada aplikacije pristupaju tekstu, slikama ili drugom sadržaju koji ste kopirali"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Prikazuj lozinke"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Prikazuje znakove nakratko dok kucate"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Ova aplikacija navodi da može da deli podatke (<xliff:g id="PERMISSION_NAME">%s</xliff:g>) sa trećim stranama"</string>
 </resources>
diff --git a/PermissionController/res/values-be/strings.xml b/PermissionController/res/values-be/strings.xml
index 7d69de8..94fc7b2 100644
--- a/PermissionController/res/values-be/strings.xml
+++ b/PermissionController/res/values-be/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Пакінуць \"У актыўным рэжыме праграмы\""</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Захаваць толькі на гэты раз"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Падрабязней"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Дазволіць доступ да ўсіх фота"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Выбраць фота"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Выбраць больш фота"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Больш не выбіраць фота"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Усё роўна не дазваляць"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Адхіліць"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> з <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Дазволіць доступ толькі да мультымедыя"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Дазволіць заўсёды"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Дазволіць толькі падчас карыстання праграмай"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Дазволіць усе фота"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Дазволіць выбраныя фота"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Заўсёды пытацца"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Не дазваляць"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Дакладнае месцазнаходжанне"</string>
@@ -424,9 +430,9 @@
     <string name="default_app_no_apps" msgid="115720991680586885">"Няма праграм"</string>
     <string name="car_default_app_selected" msgid="5416420830430644174">"Выбрана"</string>
     <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Выбрана – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string>
-    <string name="special_app_access_search_keyword" msgid="8032347212290774210">"спецыяльны доступ да праграм"</string>
-    <string name="special_app_access" msgid="5019319067120213797">"Спецыяльны доступ да праграм"</string>
-    <string name="no_special_app_access" msgid="6950277571805106247">"Няма доступу да праграм"</string>
+    <string name="special_app_access_search_keyword" msgid="8032347212290774210">"спецыяльны доступ для праграм"</string>
+    <string name="special_app_access" msgid="5019319067120213797">"Спецыяльны доступ для праграм"</string>
+    <string name="no_special_app_access" msgid="6950277571805106247">"Няма доступу для праграм"</string>
     <string name="special_app_access_no_apps" msgid="4102911722787886970">"Няма праграм"</string>
     <string name="home_missing_work_profile_support" msgid="1756855847669387977">"Працоўны профіль не падтрымліваецца"</string>
     <string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Заўвага. Калі перазапусціць прыладу з наладжанай блакіроўкай экрана, праграма не запусціцца, пакуль вы не разблакіруеце прыладу."</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да &lt;b&gt;фота, відэа, музыкі, аўдыя і файлаў&lt;/b&gt; на гэтай прыладзе?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да музыкі і аўдыя на гэтай прыладзе?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да фота і відэа на гэтай прыладзе?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Адкрыць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да большай колькасці фота?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; запісваць аўдыя?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Гэта праграма зможа запісваць аўдыя толькі падчас яе выкарыстання"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; запісваць аўдыя?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Паказваць паведамленне, калі праграмы атрымліваюць доступ да тэксту, відарысаў ці іншага змесціва, якое вы скапіравалі"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Паказваць паролі"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Падчас уводу сімвалаў на кароткі час паказваць іх"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Гэта праграма можа абагульваць з трэцімі бакамі даныя катэгорыі \"<xliff:g id="PERMISSION_NAME">%s</xliff:g>\""</string>
 </resources>
diff --git a/PermissionController/res/values-bg/strings.xml b/PermissionController/res/values-bg/strings.xml
index 1cb263d..7bf6099 100644
--- a/PermissionController/res/values-bg/strings.xml
+++ b/PermissionController/res/values-bg/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Запазване на опцията „Докато приложението се използва“"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Запазване на настройката „Само този път“"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Още информация"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Разрешаване на достъп до всички снимки"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Избиране на снимки"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Избиране на още снимки"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Без избиране на още снимки"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Забраняване въпреки това"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Отхвърляне"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> от <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Разрешаване на достъп само до мултимедията"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Разрешаване във всички случаи"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Разрешаване само докато приложението се използва"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Разрешаване на всички снимки"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Разрешаване на избраните снимки"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Извеждане на запитване всеки път"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Забраняване"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Точно местоположение"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Разр. на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; достъп до &lt;b&gt;снимките, видео- и аудиосъдърж. и другите файлове&lt;/b&gt; на у-вото?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до музиката и аудиофайловете на това устройство?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до снимките и видеоклиповете на това устройство?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Да се предостави ли достъп на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; до още снимки?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да записва аудио?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Приложението ще може да записва аудио само когато го използвате"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да записва аудио?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Показване на съобщение, когато приложенията осъществяват достъп до копирани от вас текст, изображения или друго съдържание"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Показване на паролите"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Кратко показване на знаците, докато пишете"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Това приложение посочи, че може да споделя с трети страни данни за <xliff:g id="PERMISSION_NAME">%s</xliff:g>"</string>
 </resources>
diff --git a/PermissionController/res/values-bn/strings.xml b/PermissionController/res/values-bn/strings.xml
index dab405b..658575e 100644
--- a/PermissionController/res/values-bn/strings.xml
+++ b/PermissionController/res/values-bn/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“অ্যাপ ব্যবহার করার সময়” বিকল্পটি চালু রাখুন"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“শুধুমাত্র এই সময়ে অনুমতি দিন” বিকল্পটি রাখুন"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"আরও তথ্য"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"সব ফটোতে অ্যাক্সেস দেওয়া"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ফটো বেছে নেওয়া"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"আরও ফটো বেছে নেওয়া"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"আরও ফটো বেছে নেবেন না"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"যাই হোক, অনুমতি দেবেন না"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"বাতিল করুন"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>টির মধ্যে <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> নম্বর"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"শুধু মিডিয়া অ্যাক্সেসের অনুমতি দিন"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"সব সময়ের জন্য অনুমতি দিন"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"শুধু অ্যাপ ব্যবহারের সময় অনুমতি দিন"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"সব ফটোকে অনুমতি দেওয়া"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"বাছাই করা ফটোকে অনুমতি দেওয়া"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"প্রতিবার জিজ্ঞাসা করা হবে"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"অনুমতি দেবেন না"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"সুনির্দিষ্ট লোকেশন"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"এই ডিভাইসে &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে &lt;b&gt;ফটো, ভিডিও, মিউজিক, অডিও ও অন্যান্য ফাইল&lt;/b&gt; অ্যাক্সেসের অনুমতি দেবেন?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"এই ডিভাইসে &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে মিউজিক ও অডিও ফাইল অ্যাক্সেসের অনুমতি দেবেন?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"এই ডিভাইসে &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে ফটো ও ভিডিও অ্যাক্সেসের অনুমতি দেবেন?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"আরও ফটো অ্যাক্সেস করতে &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে অনুমতি দেবেন?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে অডিও রেকর্ড করার অনুমতি দেবেন?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"আপনি এই অ্যাপ ব্যবহার করার সময়েই শুধুমাত্র সেটি অডিও রেকর্ড করতে পারবে"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে অডিও রেকর্ড করার অনুমতি দিতে চান?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"আপনার কপি করা টেক্সট, ছবি বা অন্যান্য কন্টেন্ট অ্যাপ অ্যাক্সেস করলে মেসেজ দেখায়"</string>
     <string name="show_password_title" msgid="2877269286984684659">"পাসওয়ার্ড দেখুন"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"টাইপ করার সময় অক্ষরগুলি কয়েক মুহূর্তের জন্য দেখুন"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"এই অ্যাপ, থার্ড-পার্টির সাথে <xliff:g id="PERMISSION_NAME">%s</xliff:g> সংক্রান্ত ডেটা শেয়ার করার অনুমতি চাইতে পারে"</string>
 </resources>
diff --git a/PermissionController/res/values-bs/strings.xml b/PermissionController/res/values-bs/strings.xml
index de42dcf..589495c 100644
--- a/PermissionController/res/values-bs/strings.xml
+++ b/PermissionController/res/values-bs/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Zadrži “Kada se aplikacija koristi”"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Zadrži “Samo ovaj put”"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Više informacija"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Dozvoli pristup svim fotografijama"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Odaberi fotografije"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Odaberi više fotografija"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Nemoj odabrati više fotografija"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Svejedno nemoj dozvoliti"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Odbaci"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> od <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Dozvoli pristup samo medijima"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Dozvoli sve vrijeme"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Dozvoli samo dok se aplikacija koristi"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Dozvoli sve fotografije"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Dozvoli odabrane fotografije"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Pitaj svaki put"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Nemoj dozvoliti"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Tačna lokacija"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Dozvoliti da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa &lt;b&gt;foto/video/muzičkim/audio i drugim fajlovima&lt;/b&gt; na uređaju?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Dozvoliti da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa muzici i zvuku na ovom uređaju?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Dozvoliti da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa fotografijama i videozapisima na ovom uređaju?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristup dodatnim fotografijama?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da snima zvuk?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacija će moći snimati zvuk samo za vrijeme korištenja"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snimanje zvuka?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Vidite poruku kada aplikacije pristupe tekstu, slikama ili drugom sadržaju koji ste kopirali"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Prikaži lozinke"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Kratko prikazivanje znakova dok pišete"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Aplikacija je navela da može dijeliti podatke iz kategorije <xliff:g id="PERMISSION_NAME">%s</xliff:g> s trećim stranama"</string>
 </resources>
diff --git a/PermissionController/res/values-ca/strings.xml b/PermissionController/res/values-ca/strings.xml
index 501a521..debd32c 100644
--- a/PermissionController/res/values-ca/strings.xml
+++ b/PermissionController/res/values-ca/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Només mentre s\'utilitza l\'aplicació"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Mantén el permís Només aquesta vegada"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Més informació"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Permet l\'accés a totes les fotos"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Selecciona fotos"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Selecciona més fotos"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"No seleccionis més fotos"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"No permetis de cap manera"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Ignora"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Permet l\'accés només als fitxers multimèdia"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Permet sempre"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Permet només mentre s\'utilitza l\'aplicació"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Permet totes les fotos"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Permet les fotos seleccionades"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Pregunta sempre"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"No permetis"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Ubicació exacta"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi als &lt;b&gt;vídeos, fotos, música, àudio i altres fitxers&lt;/b&gt;?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a la música i l\'àudio d\'aquest dispositiu?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a les fotos i els vídeos d\'aquest dispositiu?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a més fotos?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; gravi àudio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"L\'aplicació només podrà gravar àudio mentre l\'estiguis utilitzant"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; gravi àudio?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Mostra un missatge quan les aplicacions accedeixen al text, a les imatges o a qualsevol altre contingut que hagis copiat"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Mostra les contrasenyes"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Mostra els caràcters breument mentre escrius"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Aquesta aplicació ha indicat que és possible que comparteixi dades (<xliff:g id="PERMISSION_NAME">%s</xliff:g>) amb tercers"</string>
 </resources>
diff --git a/PermissionController/res/values-cs/strings.xml b/PermissionController/res/values-cs/strings.xml
index 376a04f..b0cd7e2 100644
--- a/PermissionController/res/values-cs/strings.xml
+++ b/PermissionController/res/values-cs/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Ponechat „Během používání aplikace“"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Ponechat možnost Pouze tentokrát"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Další informace"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Povolit přístup ke všem fotkám"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Vybrat fotky"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Vybrat více fotek"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Nevybírat více fotek"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Přesto nepovolit"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Zavřít"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> z <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Povolit pouze přístup k médiím"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Povolit vždy"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Povolit jen při používání aplikace"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Povolit všechny fotky"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Povolit vybrané fotky"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Pokaždé se zeptat"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Nepovolovat"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Přesná poloha"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k &lt;b&gt;fotkám, videím, hudbě, zvuku a dalším souborům&lt;/b&gt; v zařízení?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k hudbě a zvuku v zařízení?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k fotkám a videím v zařízení?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Udělit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k více fotkám?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nahrávat zvuk?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikace bude moci zaznamenávat zvuk, pouze když ji budete používat"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nahrávat zvuk?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Zobrazovat zprávu, když aplikace použijí text, obrázky nebo jiný obsah, který jste zkopírovali"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Zobrazovat hesla"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Při psaní se budou krátce zobrazovat zadané znaky"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Tato aplikace uvedla, že může sdílet data <xliff:g id="PERMISSION_NAME">%s</xliff:g> se třetími stranami"</string>
 </resources>
diff --git a/PermissionController/res/values-da-v33/strings.xml b/PermissionController/res/values-da-v33/strings.xml
index b2f0b09..397d80e 100644
--- a/PermissionController/res/values-da-v33/strings.xml
+++ b/PermissionController/res/values-da-v33/strings.xml
@@ -27,7 +27,7 @@
     <string name="safety_center_entry_group_with_actions_needed_content_description" msgid="2708884606775932657">"Liste. <xliff:g id="ENTRY_TITLE">%1$s</xliff:g>. Handling er påkrævet. <xliff:g id="ENTRY_SUMMARY">%2$s</xliff:g>"</string>
     <string name="safety_center_entry_group_item_content_description" msgid="7348298582877249787">"Listepunkt. <xliff:g id="ENTRY_ITEM_TITLE">%1$s</xliff:g>. <xliff:g id="ENTRY_ITEM_SUMMARY">%2$s</xliff:g>"</string>
     <string name="safety_center_entry_content_description" msgid="3639565652938224321">"<xliff:g id="ENTRY_ITEM_TITLE">%1$s</xliff:g>. <xliff:g id="ENTRY_ITEM_SUMMARY">%2$s</xliff:g>"</string>
-    <string name="safety_center_more_issues_card_title" msgid="8599419758014246517">"Se alle advarsler"</string>
+    <string name="safety_center_more_issues_card_title" msgid="8599419758014246517">"Se alle underretninger"</string>
     <string name="safety_center_more_issues_card_expand_action" msgid="7109451851052272946">"{count,plural, =1{Udvid for at se én yderligere advarsel}one{Udvid for at se # yderligere advarsel}other{Udvid for at se # yderligere advarsler}}"</string>
     <string name="safety_center_issue_card_content_description" msgid="1281390769721765363">"Underretning. <xliff:g id="ISSUE_CARD_TITLE">%1$s</xliff:g>. <xliff:g id="ISSUE_CARD_SUMMARY">%2$s</xliff:g>"</string>
     <string name="safety_center_issue_card_content_description_with_subtitle" msgid="5504040663935313539">"Underretning. <xliff:g id="ISSUE_CARD_TITLE">%1$s</xliff:g>. <xliff:g id="ISSUE_CARD_SUBTITLE">%2$s</xliff:g>. <xliff:g id="ISSUE_CARD_SUMMARY">%3$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-da/strings.xml b/PermissionController/res/values-da/strings.xml
index 559cdd9..d0a1f8d 100644
--- a/PermissionController/res/values-da/strings.xml
+++ b/PermissionController/res/values-da/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Behold \"Mens appen er i brug\""</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Behold \"Kun denne ene gang\""</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mere info"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Giv adgang til alle billeder"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Vælg billeder"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Vælg flere billeder"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Vælg ikke flere billeder"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Tillad ikke alligevel"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Luk"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> ud af <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Tillad kun adgang til mediefiler"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Tillad altid"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Tillad kun, mens appen er i brug"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Tillad alle billeder"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Tillad valgte billeder"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Spørg hver gang"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Tillad ikke"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Præcis lokation"</string>
@@ -388,7 +394,7 @@
     <string name="role_call_screening_request_description" msgid="7338511921032446006">"Der kræves ingen tilladelser"</string>
     <string name="role_automotive_navigation_label" msgid="2701890757955474751">"Standardapp til navigation"</string>
     <string name="role_automotive_navigation_short_label" msgid="5165823092506922457">"App til navigation"</string>
-    <string name="role_automotive_navigation_description" msgid="7834601873792870134">"Apps, som kan give vejledning til søgninger efter interessante steder og detaljeret rutevejledning"</string>
+    <string name="role_automotive_navigation_description" msgid="7834601873792870134">"Apps, som kan give vejledning til søgninger efter interessante lokationer og detaljeret rutevejledning"</string>
     <string name="role_automotive_navigation_request_title" msgid="7525693151489384300">"Vil du angive <xliff:g id="APP_NAME">%1$s</xliff:g> som din standardapp til navigation?"</string>
     <string name="role_automotive_navigation_request_description" msgid="7073023813249245540">"Der kræves ingen tilladelser"</string>
     <string name="role_watch_description" msgid="267003778693177779">"<xliff:g id="APP_NAME">%1$s</xliff:g> får tilladelse til at interagere med dine notifikationer og får adgang til dine tilladelser for Opkald, Sms, Kontakter og Kalender."</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til &lt;b&gt;billeder, videoer, musik, lyd og andre filer&lt;/b&gt; på denne enhed?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til musik og lyd på denne enhed?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til billeder og videoer på denne enhed?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til flere billeder?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at optage lyd?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Appen kan kun optage lyd, mens du bruger appen"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at optage lyd?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Vis en meddelelse, når apps får adgang til tekst, billeder eller andet indhold, du har kopieret"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Vis adgangskoder"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Vis kort tegnene, mens du skriver"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Denne app har angivet, at den muligvis deler <xliff:g id="PERMISSION_NAME">%s</xliff:g>-data med tredjeparter"</string>
 </resources>
diff --git a/PermissionController/res/values-de/strings.xml b/PermissionController/res/values-de/strings.xml
index 18ab6f1..33b80a0 100644
--- a/PermissionController/res/values-de/strings.xml
+++ b/PermissionController/res/values-de/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"„Wenn die App verwendet wird“ beibehalten"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\"Nur dieses Mal\" aktiviert lassen"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Weitere Infos"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Zugriff auf alle Fotos erlauben"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Fotos auswählen"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Weitere Fotos auswählen"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Keine weiteren Fotos auswählen"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Trotzdem nicht zulassen"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Schließen"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> von <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Zugriff nur auf Mediendateien zulassen"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Immer zulassen"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Zugriff nur während der Nutzung der App zulassen"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Alle Fotos erlauben"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Ausgewählte Fotos erlauben"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Jedes Mal fragen"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Nicht zulassen"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Auf genauen Standort zugreifen"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, auf &lt;b&gt;Foto-, Video-, Musik-, Audio- und andere Dateien&lt;/b&gt; auf diesem Gerät zuzugreifen?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, auf Musik- und Audiodateien auf diesem Gerät zuzugreifen?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, auf Fotos und Videos auf diesem Gerät zuzugreifen?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; Zugriff auf weitere Fotos erlauben?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, Audioaufnahmen zu machen?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Diese App kann nur Audioaufnahmen machen, solange du sie verwendest"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, Audioaufnahmen zu machen?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Eine Meldung wird angezeigt, wenn Apps auf Text, Bilder oder andere Inhalte zugreifen, die du kopiert hast"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Passwörter anzeigen"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Zeichen während der Eingabe kurz anzeigen"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Für diese App wurde angegeben, dass sie eventuell Daten vom Typ „<xliff:g id="PERMISSION_NAME">%s</xliff:g>“ an Dritte weitergibt"</string>
 </resources>
diff --git a/PermissionController/res/values-el/strings.xml b/PermissionController/res/values-el/strings.xml
index 72fb455..fba0f61 100644
--- a/PermissionController/res/values-el/strings.xml
+++ b/PermissionController/res/values-el/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Διατήρηση της επιλογής \"Όταν χρησιμοποιείται η εφαρμογή\""</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Διατήρηση μόνο αυτήν τη φορά"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Περισσότερα"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Να επιτρέπεται η πρόσβαση σε όλες τις φωτογραφίες"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Επιλογή φωτογραφιών"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Επιλογή περισσότερων φωτογραφιών"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Να μην επιλεγούν περισσότερες φωτογραφίες"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Να μην επιτρέπεται καθόλου"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Παράβλεψη"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> από <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Να επιτρέπεται η διαχείριση μόνο των μέσων"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Να επιτρέπεται πάντα"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Μόνο με τη χρήση της εφαρμογής"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Να επιτρέπονται όλες οι φωτογραφίες"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Να επιτρέπονται οι επιλεγμένες φωτογραφίες"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Να ερωτώμαι κάθε φορά"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Να μην επιτρέπεται"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Ακριβής τοποθεσία"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση σε &lt;b&gt;φωτογραφίες, βίντεο, μουσική, ήχο και άλλα αρχεία&lt;/b&gt; της συσκευής;"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση στη μουσική και στα αρχεία ήχου αυτής της συσκευής;"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση στις φωτογραφίες και τα βίντεο αυτής της συσκευής;"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Εκχώρηση πρόσβασης στο &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; σε περισσότερες φωτογραφίες;"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Να επιτρέπεται στο &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η εγγραφή ήχου;"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Αυτή η εφαρμογή θα μπορεί να εγγράφει ήχο μόνο όταν τη χρησιμοποιείτε"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Να επιτρέπεται στο &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η εγγραφή ήχου;"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Να εμφανίζεται ένα μήνυμα όταν οι εφαρμογές αποκτούν πρόσβαση σε κείμενο, εικόνες ή άλλο περιεχόμενο που έχετε αντιγράψει"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Εμφάνιση κωδικών πρόσβασης"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Σύντομη εμφάνιση χαρακτήρων κατά την πληκτρολόγηση"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Αυτή η εφαρμογή δηλώνει ότι ενδέχεται να κοινοποιεί δεδομένα που αφορούν την άδεια \"<xliff:g id="PERMISSION_NAME">%s</xliff:g>\" σε τρίτα μέρη"</string>
 </resources>
diff --git a/PermissionController/res/values-en-rAU/strings.xml b/PermissionController/res/values-en-rAU/strings.xml
index 643596a..787452e 100644
--- a/PermissionController/res/values-en-rAU/strings.xml
+++ b/PermissionController/res/values-en-rAU/strings.xml
@@ -28,10 +28,14 @@
     <string name="uninstall_or_disable" msgid="4496612999740858933">"Uninstall or disable"</string>
     <string name="app_not_found_dlg_title" msgid="6029482906093859756">"App not found"</string>
     <string name="grant_dialog_button_deny" msgid="88262611492697192">"Don\'t allow"</string>
-    <string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Don’t allow &amp; don’t ask again"</string>
+    <string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Don’t allow and don’t ask again"</string>
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Keep \'While the app is in use\'"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Keep \'Only this time\'"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"More info"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Allow access to all photos"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Select photos"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Select more photos"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Don’t select more photos"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Don’t allow anyway"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Dismiss"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> of <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Allow access to media only"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Allow all the time"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Allow only while using the app"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Allow all photos"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Allow selected photos"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Ask every time"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Don\'t allow"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Precise location"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;photos, videos, music, audio and other files&lt;/b&gt; on this device?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access music and audio on this device?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and videos on this device?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Grant &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; access to more photos?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"The app will only be able to record audio while you’re using the app"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Show a message when apps access text, images or other content that you’ve copied"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Show passwords"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Display characters briefly as you type"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"This app stated that it may share <xliff:g id="PERMISSION_NAME">%s</xliff:g> data with third parties"</string>
 </resources>
diff --git a/PermissionController/res/values-en-rCA-television/strings.xml b/PermissionController/res/values-en-rCA-television/strings.xml
index 923ce15..aed6c88 100644
--- a/PermissionController/res/values-en-rCA-television/strings.xml
+++ b/PermissionController/res/values-en-rCA-television/strings.xml
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="grant_dialog_button_deny_dont_ask_again" msgid="747769682501286250">"Deny and don\'t ask again"</string>
     <string name="grant_dialog_how_to_change" msgid="997462845048160559">"You can change this later in Settings &gt; Apps"</string>
-    <string name="current_permission_template" msgid="6240787325714651204">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="current_permission_template" msgid="6240787325714651204">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
     <string name="preference_show_system_apps" msgid="4262140518693221093">"Show system apps"</string>
     <string name="app_permissions_decor_title" msgid="7438716722786036814">"App permissions"</string>
     <string name="manage_permissions_decor_title" msgid="4138423885439613577">"App permissions"</string>
diff --git a/PermissionController/res/values-en-rCA-v33/strings.xml b/PermissionController/res/values-en-rCA-v33/strings.xml
index e73e4b5..6cd114b 100644
--- a/PermissionController/res/values-en-rCA-v33/strings.xml
+++ b/PermissionController/res/values-en-rCA-v33/strings.xml
@@ -16,8 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="role_dialer_request_description" msgid="6188305064871543419">"This app will be allowed to send you notifications, and will be given access to your camera, contacts, microphone, phone and SMS"</string>
-    <string name="role_sms_request_description" msgid="1506966389698625395">"This app will be allowed to send you notifications, and will be given access to your camera, contacts, files, microphone, phone and SMS"</string>
+    <string name="role_dialer_request_description" msgid="6188305064871543419">"This app will be allowed to send you Notifications, and will be given access to your Camera, Contacts, Microphone, Phone, and SMS"</string>
+    <string name="role_sms_request_description" msgid="1506966389698625395">"This app will be allowed to send you Notifications, and will be given access to your Camera, Contacts, Files, Microphone, Phone, and SMS"</string>
     <string name="permission_description_summary_storage" msgid="1917071243213043858">"Apps with this permission can access all files on this device"</string>
     <string name="work_policy_title" msgid="832967780713677409">"Your work policy info"</string>
     <string name="work_policy_summary" msgid="3886113358084963931">"Settings managed by your IT admin"</string>
diff --git a/PermissionController/res/values-en-rCA-watch/strings.xml b/PermissionController/res/values-en-rCA-watch/strings.xml
index 70643f5..adecb65 100644
--- a/PermissionController/res/values-en-rCA-watch/strings.xml
+++ b/PermissionController/res/values-en-rCA-watch/strings.xml
@@ -16,8 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5709879604352260492">"Deny. Don\'t ask again"</string>
-    <string name="current_permission_template" msgid="6634462553790549887">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
+    <string name="grant_dialog_button_deny_dont_ask_again" msgid="5709879604352260492">"Deny, don\'t ask again"</string>
+    <string name="current_permission_template" msgid="6634462553790549887">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
     <string name="preference_show_system_apps" msgid="1055740303992024300">"Show system apps"</string>
     <string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Can\'t be changed"</string>
     <string name="generic_yes" msgid="2489207724988649846">"Yes"</string>
diff --git a/PermissionController/res/values-en-rCA/strings.xml b/PermissionController/res/values-en-rCA/strings.xml
index 643596a..7f618a7 100644
--- a/PermissionController/res/values-en-rCA/strings.xml
+++ b/PermissionController/res/values-en-rCA/strings.xml
@@ -27,11 +27,15 @@
     <string name="off" msgid="1438489226422866263">"Off"</string>
     <string name="uninstall_or_disable" msgid="4496612999740858933">"Uninstall or disable"</string>
     <string name="app_not_found_dlg_title" msgid="6029482906093859756">"App not found"</string>
-    <string name="grant_dialog_button_deny" msgid="88262611492697192">"Don\'t allow"</string>
+    <string name="grant_dialog_button_deny" msgid="88262611492697192">"Don’t allow"</string>
     <string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Don’t allow &amp; don’t ask again"</string>
-    <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Keep \'While the app is in use\'"</string>
-    <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Keep \'Only this time\'"</string>
+    <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Keep “While the app is in use”"</string>
+    <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Keep “Only this time”"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"More info"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Allow access to all photos"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Select photos"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Select more photos"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Don’t select more photos"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Don’t allow anyway"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Dismiss"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> of <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -56,16 +60,16 @@
     <string name="app_permissions" msgid="3369917736607944781">"App permissions"</string>
     <string name="unused_apps" msgid="2058057455175955094">"Unused apps"</string>
     <string name="no_unused_apps" msgid="12809387670415295">"No unused apps"</string>
-    <string name="zero_unused_apps" msgid="9024448554157499748">"Zero unused apps"</string>
+    <string name="zero_unused_apps" msgid="9024448554157499748">"0 unused apps"</string>
     <string name="review_permission_decisions" msgid="309559429150613632">"Recent permission decisions"</string>
     <string name="review_permission_decisions_view_all" msgid="90391040431566130">"View all recent permission decisions"</string>
     <string name="review_permission_decisions_empty" msgid="8120775336417279806">"No recent permission decisions"</string>
-    <string name="auto_permission_manager_summary" msgid="9157438376234301354">"Manage data access to calendar, call logs and more"</string>
+    <string name="auto_permission_manager_summary" msgid="9157438376234301354">"Manage data access to calendar, call logs, and more"</string>
     <string name="granted_permission_decision" msgid="7824827491551861365">"You gave <xliff:g id="APP_NAME">%1$s</xliff:g> access to <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string>
     <string name="denied_permission_decision" msgid="5308961501779563781">"You denied <xliff:g id="APP_NAME">%1$s</xliff:g> access to <xliff:g id="PERMISSION_NAME">%2$s</xliff:g>"</string>
     <string name="days_ago" msgid="6650359081551335629">"{count,plural, =0{Today}=1{1 day ago}other{# days ago}}"</string>
     <string name="app_disable_dlg_positive" msgid="7418444149981904940">"Disable app"</string>
-    <string name="app_disable_dlg_text" msgid="3126943217146120240">"If you disable this app, Android and other apps may no longer function as intended. Remember that you can’t delete this app as it came pre-installed on your device. By disabling it, you turn this app off and hide it on your device."</string>
+    <string name="app_disable_dlg_text" msgid="3126943217146120240">"If you disable this app, Android and other apps may no longer function as intended. Keep in mind, you can’t delete this app since it came pre-installed on your device. By disabling, you turn this app off and hide it on your device."</string>
     <string name="app_permission_manager" msgid="3903811137630909550">"Permission manager"</string>
     <string name="never_ask_again" msgid="4728762438198560329">"Don\'t ask again"</string>
     <string name="no_permissions" msgid="3881676756371148563">"No permissions"</string>
@@ -73,8 +77,8 @@
     <string name="app_permissions_info_button_label" msgid="7633312050729974623">"Open app info"</string>
     <string name="additional_permissions_more" msgid="5681220714755304407">"{count,plural, =1{# more}other{# more}}"</string>
     <string name="old_sdk_deny_warning" msgid="2382236998845153919">"This app was designed for an older version of Android. Denying permission may cause it to no longer function as intended."</string>
-    <string name="storage_supergroup_warning_allow" msgid="103093462784523190">"This app was designed for an older version of Android. If you allow this permission, then access to all storage (including photos, videos, music, audio and other files) will be allowed."</string>
-    <string name="storage_supergroup_warning_deny" msgid="6420765672683284347">"This app was designed for an older version of Android. If you deny this permission, then access to all storage (including photos, videos, music, audio and other files) will be denied."</string>
+    <string name="storage_supergroup_warning_allow" msgid="103093462784523190">"This app was designed for an older version of Android. If you allow this permission, then access to all storage (including photos, videos, music, audio, and other files) will be allowed."</string>
+    <string name="storage_supergroup_warning_deny" msgid="6420765672683284347">"This app was designed for an older version of Android. If you deny this permission, then access to all storage (including photos, videos, music, audio, and other files) will be denied."</string>
     <string name="default_permission_description" msgid="4624464917726285203">"perform an unknown action"</string>
     <string name="app_permissions_group_summary" msgid="8788419008958284002">"<xliff:g id="COUNT_0">%1$d</xliff:g> of <xliff:g id="COUNT_1">%2$d</xliff:g> apps allowed"</string>
     <string name="app_permissions_group_summary2" msgid="4329922444840521150">"<xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g> apps allowed"</string>
@@ -104,18 +108,18 @@
     <!-- no translation found for background_access_chooser_dialog_choices:2 (4305536986042401191) -->
     <string name="permission_access_always" msgid="1474641821883823446">"Allow all the time"</string>
     <string name="permission_access_only_foreground" msgid="7801170728159326195">"Allow only while using the app"</string>
-    <string name="permission_access_never" msgid="4647014230217936900">"Don\'t allow"</string>
+    <string name="permission_access_never" msgid="4647014230217936900">"Don’t allow"</string>
     <string name="loading" msgid="4789365003890741082">"Loading…"</string>
     <string name="all_permissions" msgid="6911125611996872522">"All permissions"</string>
     <string name="other_permissions" msgid="2901186127193849594">"Other app capabilities"</string>
     <string name="permission_request_title" msgid="8790310151025020126">"Permission request"</string>
     <string name="screen_overlay_title" msgid="6977038513913222078">"Screen overlay detected"</string>
-    <string name="screen_overlay_message" msgid="5622563069757142102">"To change this permission setting, you have to turn off the screen overlay first from Settings &gt; Apps"</string>
+    <string name="screen_overlay_message" msgid="5622563069757142102">"To change this permission setting, you first have to turn off the screen overlay from Settings &gt; Apps"</string>
     <string name="screen_overlay_button" msgid="4655005928054025250">"Open settings"</string>
     <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
     <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Install/Uninstall actions not supported on Wear."</string>
     <string name="permission_review_title_template_install" msgid="1284337937156289081">"Choose what to allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access"</string>
-    <string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; has been updated. Choose what access to allow this app."</string>
+    <string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; has been updated. Choose what to allow this app to access."</string>
     <string name="review_button_cancel" msgid="2191147944056548886">"Cancel"</string>
     <string name="review_button_continue" msgid="2527918375047602199">"Continue"</string>
     <string name="new_permissions_category" msgid="552995090178417611">"New permissions"</string>
@@ -127,7 +131,7 @@
     <string name="permission_group_usage_title" msgid="2595013198075285173">"<xliff:g id="PERMGROUP">%1$s</xliff:g> usage"</string>
     <string name="perm_usage_adv_info_title" msgid="3357831829538873708">"See other permissions"</string>
     <string name="perm_usage_adv_info_summary_2_items" msgid="3702175198750127822">"<xliff:g id="PERMGROUP_0">%1$s</xliff:g>, <xliff:g id="PERMGROUP_1">%2$s</xliff:g>"</string>
-    <string name="perm_usage_adv_info_summary_more_items" msgid="949055326299562218">"<xliff:g id="PERMGROUP_0">%1$s</xliff:g>, <xliff:g id="PERMGROUP_1">%2$s</xliff:g> and <xliff:g id="NUM">%3$s</xliff:g> more"</string>
+    <string name="perm_usage_adv_info_summary_more_items" msgid="949055326299562218">"<xliff:g id="PERMGROUP_0">%1$s</xliff:g>, <xliff:g id="PERMGROUP_1">%2$s</xliff:g>, and <xliff:g id="NUM">%3$s</xliff:g> more"</string>
     <string name="permission_group_usage_subtitle_24h" msgid="5120155996322114181">"Timeline of when apps used your <xliff:g id="PERMGROUP">%1$s</xliff:g> in the past 24 hours"</string>
     <string name="permission_group_usage_subtitle_7d" msgid="1465828402260324654">"Timeline of when apps used your <xliff:g id="PERMGROUP">%1$s</xliff:g> in the past 7 days"</string>
     <string name="permission_usage_access_dialog_subtitle" msgid="4171772805196955753">"When this app used your <xliff:g id="PERMGROUP">%1$s</xliff:g> permission"</string>
@@ -186,8 +190,10 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Allow access to media only"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Allow all the time"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Allow only while using the app"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Allow all photos"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Allow selected photos"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Ask every time"</string>
-    <string name="app_permission_button_deny" msgid="6016454069832050300">"Don\'t allow"</string>
+    <string name="app_permission_button_deny" msgid="6016454069832050300">"Don’t allow"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Precise location"</string>
     <string name="approximate_image_description" msgid="938803699637069884">"Approximate location"</string>
     <string name="app_permission_location_accuracy" msgid="7166912915040018669">"Use precise location"</string>
@@ -196,18 +202,18 @@
     <string name="app_permission_header" msgid="2951363137032603806">"<xliff:g id="PERM">%1$s</xliff:g> access for this app"</string>
     <string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"See all <xliff:g id="APP">%1$s</xliff:g> permissions"</string>
     <string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"See all apps with this permission"</string>
-    <string name="assistant_mic_label" msgid="1011432357152323896">"Show Assistant microphone usage"</string>
+    <string name="assistant_mic_label" msgid="1011432357152323896">"Show assistant microphone usage"</string>
     <string name="unused_apps_category_title" msgid="2988455616845243901">"Unused app settings"</string>
     <string name="auto_revoke_label" msgid="5068393642936571656">"Remove permissions if app isn’t used"</string>
     <string name="unused_apps_label" msgid="2595428768404901064">"Remove permissions and free up space"</string>
     <string name="unused_apps_label_v2" msgid="7058776770056517980">"Pause app activity if unused"</string>
-    <string name="unused_apps_summary" msgid="8839466950318403115">"Remove permissions, delete temporary files and stop notifications"</string>
+    <string name="unused_apps_summary" msgid="8839466950318403115">"Remove permissions, delete temporary files, and stop notifications"</string>
     <string name="auto_revoke_summary" msgid="5867548789805911683">"To protect your data, permissions for this app will be removed if the app is unused for a few months."</string>
     <string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"To protect your data, if the app is unused for a few months, the following permissions will be removed: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
     <string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"To protect your data, permissions have been removed from apps that you haven’t used in a few months."</string>
     <string name="auto_revoke_open_app_message" msgid="8075556291711205039">"If you want to allow permissions again, open the app."</string>
     <string name="auto_revoke_disabled" msgid="8697684442991567188">"Automatic removal is currently disabled for this app."</string>
-    <string name="auto_revocable_permissions_none" msgid="8334929619113991466">"No auto-revocable permissions are currently granted"</string>
+    <string name="auto_revocable_permissions_none" msgid="8334929619113991466">"No auto revocable permissions are currently granted"</string>
     <string name="auto_revocable_permissions_one" msgid="5299112369449458176">"<xliff:g id="PERM">%1$s</xliff:g> permission will be removed."</string>
     <string name="auto_revocable_permissions_two" msgid="4874067408752041716">"<xliff:g id="PERM_0">%1$s</xliff:g> and <xliff:g id="PERM_1">%2$s</xliff:g> permissions will be removed."</string>
     <string name="auto_revocable_permissions_many" msgid="1521807896206032992">"Permissions that will be removed: <xliff:g id="PERMS">%1$s</xliff:g>."</string>
@@ -221,27 +227,27 @@
     <string name="last_opened_category_title" msgid="7871347400611202595">"Last opened more than <xliff:g id="NUMBER">%s</xliff:g> months ago"</string>
     <string name="last_opened_summary" msgid="5248984030024968808">"App last opened on <xliff:g id="DATE">%s</xliff:g>"</string>
     <string name="last_opened_summary_short" msgid="1646067226191176825">"Last opened <xliff:g id="DATE">%s</xliff:g>"</string>
-    <string name="app_permission_footer_special_file_access" msgid="1884202176147657788">"If you allow management of all files, this app can access, modify and delete any files in common storage on this device or connected storage devices. The app may access files without asking you."</string>
+    <string name="app_permission_footer_special_file_access" msgid="1884202176147657788">"If you allow management of all files, this app can access, modify, and delete any files in common storage on this device or connected storage devices. The app may access files without asking you."</string>
     <string name="special_file_access_dialog" msgid="583804114020740610">"Allow this app to access, modify and delete files on the device, or any connected storage devices? This app may access files without asking you."</string>
     <string name="permission_description_summary_generic" msgid="5401399408814903391">"Apps with this permission can <xliff:g id="DESCRIPTION">%1$s</xliff:g>"</string>
-    <string name="permission_description_summary_activity_recognition" msgid="2652850576497070146">"Apps with this permission can access your physical activity, such as walking, cycling, driving, step count and more"</string>
+    <string name="permission_description_summary_activity_recognition" msgid="2652850576497070146">"Apps with this permission can access your physical activity, like walking, biking, driving, step count, and more"</string>
     <string name="permission_description_summary_calendar" msgid="103329982944411010">"Apps with this permission can access your calendar"</string>
-    <string name="permission_description_summary_call_log" msgid="7321437186317577624">"Apps with this permission can read and write phone call logs"</string>
+    <string name="permission_description_summary_call_log" msgid="7321437186317577624">"Apps with this permission can read and write phone call log"</string>
     <string name="permission_description_summary_camera" msgid="108004375101882069">"Apps with this permission can take pictures and record video"</string>
     <string name="permission_description_summary_contacts" msgid="2337798886460408996">"Apps with this permission can access your contacts"</string>
     <string name="permission_description_summary_location" msgid="2817531799933480694">"Apps with this permission can access this device\'s location"</string>
-    <string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"Apps with this permission can find, connect to and determine the relative position of nearby devices"</string>
+    <string name="permission_description_summary_nearby_devices" msgid="8269183818275073741">"Apps with this permission can find, connect to, and determine the relative position of nearby devices"</string>
     <string name="permission_description_summary_microphone" msgid="630834800308329907">"Apps with this permission can record audio"</string>
     <string name="permission_description_summary_phone" msgid="4515277217435233619">"Apps with this permission can make and manage phone calls"</string>
     <string name="permission_description_summary_sensors" msgid="1836045815643119949">"Apps with this permission can access sensor data about your vital signs"</string>
     <string name="permission_description_summary_sms" msgid="725999468547768517">"Apps with this permission can send and view SMS messages"</string>
-    <string name="permission_description_summary_storage" msgid="6575759089065303346">"Apps with this permission can access photos, media and files on your device"</string>
+    <string name="permission_description_summary_storage" msgid="6575759089065303346">"Apps with this permission can access photos, media, and files on your device"</string>
     <string name="permission_description_summary_read_media_aural" msgid="3354728149930482199">"Apps with this permission can access music and other audio files on this device"</string>
     <string name="permission_description_summary_read_media_visual" msgid="4991801977881732641">"Apps with this permission can access photos and videos on this device"</string>
     <string name="app_permission_most_recent_summary" msgid="4292074449384040590">"Last access: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
-    <string name="app_permission_most_recent_denied_summary" msgid="7659497197737708112">"Currently denied/Last access: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+    <string name="app_permission_most_recent_denied_summary" msgid="7659497197737708112">"Currently denied / Last access: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
     <string name="app_permission_never_accessed_summary" msgid="401346181461975090">"Never accessed"</string>
-    <string name="app_permission_never_accessed_denied_summary" msgid="6596000497490905146">"Denied/Never accessed"</string>
+    <string name="app_permission_never_accessed_denied_summary" msgid="6596000497490905146">"Denied / Never accessed"</string>
     <string name="allowed_header" msgid="7769277978004790414">"Allowed"</string>
     <string name="allowed_always_header" msgid="6455903312589013545">"Allowed all the time"</string>
     <string name="allowed_foreground_header" msgid="6845655788447833353">"Allowed only while in use"</string>
@@ -259,7 +265,7 @@
     <string name="auto_revoke_permission_reminder_notification_title_many" msgid="6062217713645069960">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> unused apps"</string>
     <string name="auto_revoke_permission_reminder_notification_content" msgid="4492228990462107487">"Permissions removed to protect your privacy. Tap to review"</string>
     <string name="auto_revoke_permission_notification_title" msgid="2629844160853454657">"Permissions removed for unused apps"</string>
-    <string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"Some apps haven’t been used for a few months. Tap to review."</string>
+    <string name="auto_revoke_permission_notification_content" msgid="5125990886047799375">"Some apps haven’t been used in a few months. Tap to review."</string>
     <string name="unused_apps_notification_title" msgid="4314832015894238019">"{count,plural, =1{# unused app}other{# unused apps}}"</string>
     <string name="unused_apps_notification_content" msgid="9195026773244581246">"Permissions and temporary files have been removed and notifications were stopped. Tap to review."</string>
     <string name="unused_apps_safety_center_card_title" msgid="5638409355530099149">"Review apps with permissions removed"</string>
@@ -271,7 +277,7 @@
     <string name="post_drive_permission_decision_reminder_summary_1_app_multi_permission" msgid="4080701771111456927">"While driving, you granted <xliff:g id="COUNT">%1$d</xliff:g> permissions to <xliff:g id="APP">%2$s</xliff:g>"</string>
     <string name="post_drive_permission_decision_reminder_summary_multi_apps" msgid="5253882771252863902">"{count,plural, =1{While driving, you gave <xliff:g id="APP_0">%1$s</xliff:g> &amp; # other app access}other{While driving, you gave <xliff:g id="APP_1">%1$s</xliff:g> &amp; # other apps access}}"</string>
     <string name="go_to_settings" msgid="1053735612211228335">"Go to Settings"</string>
-    <string name="auto_revoke_setting_subtitle" msgid="8631720570723050460">"Some apps haven’t been used for a few months"</string>
+    <string name="auto_revoke_setting_subtitle" msgid="8631720570723050460">"Some apps haven’t been used in a few months"</string>
     <string name="permissions_removed_category_title" msgid="1064754271178447643">"Removed permissions"</string>
     <string name="permission_removed_page_title" msgid="2627436155091001209">"Permissions removed"</string>
     <string name="all_unused_apps_category_title" msgid="755663524704745414">"All unused apps"</string>
@@ -280,37 +286,37 @@
     <string name="background_location_access_reminder_notification_title" msgid="1140797924301941262">"<xliff:g id="APP_NAME">%s</xliff:g> got your location in the background"</string>
     <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"This app can always access your location. Tap to change."</string>
     <string name="notification_listener_reminder_notification_title" msgid="3747210460187479091">"Review app with access to your notifications"</string>
-    <string name="notification_listener_reminder_notification_content" msgid="831476101108863427">"<xliff:g id="APP_NAME">%s</xliff:g> can dismiss, act on and access content inside your notifications"</string>
-    <string name="notification_listener_warning_card_content" msgid="7840973324284115893">"This app can dismiss, act on and access content inside your notifications. Some apps require this access to function as intended."</string>
+    <string name="notification_listener_reminder_notification_content" msgid="831476101108863427">"<xliff:g id="APP_NAME">%s</xliff:g> can dismiss, act on, and access content inside your notifications"</string>
+    <string name="notification_listener_warning_card_content" msgid="7840973324284115893">"This app can dismiss, act on, and access content inside your notifications. Some apps require this access to function as intended."</string>
     <string name="notification_listener_remove_access_button_label" msgid="7101898782417817097">"Remove access"</string>
     <string name="notification_listener_review_app_button_label" msgid="3433073281029143924">"See more options"</string>
     <string name="notification_listener_remove_access_success_label" msgid="2477611529875633107">"Access removed"</string>
     <string name="accessibility_access_reminder_notification_title" msgid="2971317234668807566">"Review app with full device access"</string>
     <string name="accessibility_access_reminder_notification_content" msgid="7389454158175306720">"<xliff:g id="APP_NAME">%s</xliff:g> can view your screen and perform actions on your device. Accessibility apps need this type of access to function as intended."</string>
-    <string name="accessibility_access_warning_card_content" msgid="4370327190293217358">"This app can view your screen and perform actions on your device. Accessibility apps need this type of access to function as intended, but check the app and make sure that you trust it."</string>
+    <string name="accessibility_access_warning_card_content" msgid="4370327190293217358">"This app can view your screen and perform actions on your device. Accessibility apps need this type of access to function as intended, but check the app and make sure you trust it."</string>
     <string name="accessibility_remove_access_button_label" msgid="44145801526711640">"Remove access"</string>
     <string name="accessibility_show_all_apps_button_label" msgid="960067249326392280">"View apps with full access"</string>
     <string name="accessibility_remove_access_success_label" msgid="4380995302917014670">"Access removed"</string>
-    <string name="safety_center_notification_app_label" msgid="2457720616141926534">"Android system"</string>
+    <string name="safety_center_notification_app_label" msgid="2457720616141926534">"Android System"</string>
     <string name="auto_revoke_after_notification_title" msgid="5417761027669887431">"App permissions removed to protect privacy"</string>
-    <string name="auto_revoke_after_notification_content_one" msgid="6804038707453662753">"<xliff:g id="APP_NAME">%s</xliff:g> hasn’t been used for a few months. Tap to review."</string>
-    <string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"<xliff:g id="APP_NAME">%s</xliff:g> and one other app haven’t been used for a few months. Tap to review."</string>
-    <string name="auto_revoke_after_notification_content_many" msgid="4774106206289751220">"<xliff:g id="APP_NAME">%1$s</xliff:g> and <xliff:g id="NUMBER_OF_APPS">%2$s</xliff:g> other apps haven’t been used for a few months. Tap to review."</string>
-    <string name="auto_revoke_before_notification_title_one" msgid="6758024954464359876">"One app is unused"</string>
+    <string name="auto_revoke_after_notification_content_one" msgid="6804038707453662753">"<xliff:g id="APP_NAME">%s</xliff:g> hasn’t been used in a few months. Tap to review."</string>
+    <string name="auto_revoke_after_notification_content_two" msgid="9108709764831425172">"<xliff:g id="APP_NAME">%s</xliff:g> and 1 other app haven’t been used in a few months. Tap to review."</string>
+    <string name="auto_revoke_after_notification_content_many" msgid="4774106206289751220">"<xliff:g id="APP_NAME">%1$s</xliff:g> and <xliff:g id="NUMBER_OF_APPS">%2$s</xliff:g> other apps haven’t been used in a few months. Tap to review."</string>
+    <string name="auto_revoke_before_notification_title_one" msgid="6758024954464359876">"1 app is unused"</string>
     <string name="auto_revoke_before_notification_title_many" msgid="4415543943846385685">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> apps are unused"</string>
     <string name="auto_revoke_before_notification_content_one" msgid="1156635373417068822">"Permissions will be removed to protect your privacy. Tap to review."</string>
     <string name="unused_apps_title" msgid="8589298917717872239">"Unused apps"</string>
     <string name="unused_apps_subtitle_after" msgid="2034267519506357898">"Permissions removed from"</string>
     <string name="unused_apps_subtitle_before" msgid="5233302577076132427">"Permissions will be removed from"</string>
     <string name="unused_permissions_subtitle_two" msgid="2207266295008423015">"<xliff:g id="PERM_NAME_0">%1$s</xliff:g> and <xliff:g id="PERM_NAME_1">%2$s</xliff:g>"</string>
-    <string name="unused_permissions_subtitle_many" msgid="4387289202207450238">"<xliff:g id="PERM_NAME_0">%1$s</xliff:g>, <xliff:g id="PERM_NAME_1">%2$s</xliff:g> and <xliff:g id="NUMBER_OF_PERMISSIONS">%3$s</xliff:g> more"</string>
-    <string name="unused_app_permissions_removed_summary" msgid="6779039455326071033">"To protect your data, permissions have been removed from apps that you haven’t used for a few months"</string>
-    <string name="unused_app_permissions_removed_summary_some" msgid="5080490037831563441">"To protect your data, permissions have been removed from some apps that you haven’t used for a few months"</string>
-    <string name="one_unused_app_summary" msgid="7831913934488881991">"1 app hasn’t been used for a few months"</string>
+    <string name="unused_permissions_subtitle_many" msgid="4387289202207450238">"<xliff:g id="PERM_NAME_0">%1$s</xliff:g>, <xliff:g id="PERM_NAME_1">%2$s</xliff:g>, and <xliff:g id="NUMBER_OF_PERMISSIONS">%3$s</xliff:g> more"</string>
+    <string name="unused_app_permissions_removed_summary" msgid="6779039455326071033">"To protect your data, permissions have been removed from apps that you haven’t used in a few months"</string>
+    <string name="unused_app_permissions_removed_summary_some" msgid="5080490037831563441">"To protect your data, permissions have been removed from some apps that you haven’t used in a few months"</string>
+    <string name="one_unused_app_summary" msgid="7831913934488881991">"1 app hasen’t been used for a few months"</string>
     <string name="num_unused_apps_summary" msgid="1870719749940571227">"<xliff:g id="NUMBER_OF_APPS">%s</xliff:g> apps haven’t been used for a few months"</string>
     <string name="permission_subtitle_only_in_foreground" msgid="9068389431267377564">"Only while app is in use"</string>
     <string name="permission_subtitle_media_only" msgid="8917869683764720717">"Media"</string>
-    <string name="permission_subtitle_all_files" msgid="4982613338298067862">"All files"</string>
+    <string name="permission_subtitle_all_files" msgid="4982613338298067862">"All Files"</string>
     <string name="permission_subtitle_background" msgid="8916750995309083180">"Allowed all the time"</string>
     <string name="app_perms_24h_access" msgid="99069906850627181">"Last accessed <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
     <string name="app_perms_24h_access_yest" msgid="5411926024794555022">"Last accessed yesterday at <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
@@ -327,11 +333,11 @@
     <string name="app_perms_7d_access_media_only" msgid="1031096653668235200">"Last accessed <xliff:g id="TIME_DATE_0">%1$s</xliff:g> at <xliff:g id="TIME_DATE_1">%2$s</xliff:g> • Media"</string>
     <string name="app_perms_content_provider_24h_media_only" msgid="7797963000596179491">"Accessed in past 24 hours • Media"</string>
     <string name="app_perms_content_provider_7d_media_only" msgid="8446239884570262243">"Accessed in past 7 days • Media"</string>
-    <string name="app_perms_24h_access_all_files" msgid="8902360456978159091">"Last accessed <xliff:g id="TIME_DATE">%1$s</xliff:g> • All files"</string>
-    <string name="app_perms_24h_access_yest_all_files" msgid="5708424073126844909">"Last accessed yesterday at <xliff:g id="TIME_DATE">%1$s</xliff:g> • All files"</string>
-    <string name="app_perms_7d_access_all_files" msgid="8246193786397635824">"Last accessed <xliff:g id="TIME_DATE_0">%1$s</xliff:g> at <xliff:g id="TIME_DATE_1">%2$s</xliff:g> • All files"</string>
-    <string name="app_perms_content_provider_24h_all_files" msgid="573104317727770850">"Accessed in past 24 hours • All files"</string>
-    <string name="app_perms_content_provider_7d_all_files" msgid="7962416229708835558">"Accessed in past 7 days • All files"</string>
+    <string name="app_perms_24h_access_all_files" msgid="8902360456978159091">"Last accessed <xliff:g id="TIME_DATE">%1$s</xliff:g> • All Files"</string>
+    <string name="app_perms_24h_access_yest_all_files" msgid="5708424073126844909">"Last accessed yesterday at <xliff:g id="TIME_DATE">%1$s</xliff:g> • All Files"</string>
+    <string name="app_perms_7d_access_all_files" msgid="8246193786397635824">"Last accessed <xliff:g id="TIME_DATE_0">%1$s</xliff:g> at <xliff:g id="TIME_DATE_1">%2$s</xliff:g> • All Files"</string>
+    <string name="app_perms_content_provider_24h_all_files" msgid="573104317727770850">"Accessed in past 24 hours • All Files"</string>
+    <string name="app_perms_content_provider_7d_all_files" msgid="7962416229708835558">"Accessed in past 7 days • All Files"</string>
     <string name="no_permissions_allowed" msgid="6081976856354669209">"No permissions allowed"</string>
     <string name="no_permissions_denied" msgid="8159923922804043282">"No permissions denied"</string>
     <string name="no_apps_allowed" msgid="7718822655254468631">"No apps allowed"</string>
@@ -342,27 +348,27 @@
     <string name="settings" msgid="5409109923158713323">"Settings"</string>
     <string name="accessibility_service_dialog_title_single" msgid="7956432823014102366">"<xliff:g id="SERVICE_NAME">%s</xliff:g> has full access to your device"</string>
     <string name="accessibility_service_dialog_title_multiple" msgid="5527879210683548175">"<xliff:g id="NUM_SERVICES">%s</xliff:g> accessibility apps have full access to your device"</string>
-    <string name="accessibility_service_dialog_bottom_text_single" msgid="1128666197822205958">"<xliff:g id="SERVICE_NAME">%s</xliff:g> can view your screen, actions and inputs, perform actions and control the display."</string>
-    <string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"These apps can view your screen, actions and inputs, perform actions, and control the display."</string>
+    <string name="accessibility_service_dialog_bottom_text_single" msgid="1128666197822205958">"<xliff:g id="SERVICE_NAME">%s</xliff:g> can view your screen, actions, and inputs, perform actions, and control the display."</string>
+    <string name="accessibility_service_dialog_bottom_text_multiple" msgid="7009848932395519852">"These apps can view your screen, actions, and inputs, perform actions, and control the display."</string>
     <string name="role_assistant_label" msgid="4727586018198208128">"Default digital assistant app"</string>
     <string name="role_assistant_short_label" msgid="3369003713187703399">"Digital assistant app"</string>
-    <string name="role_assistant_description" msgid="6622458130459922952">"Assist apps can help you based on information from the screen that you’re viewing. Some apps support both launcher and voice input services to give you integrated assistance."</string>
+    <string name="role_assistant_description" msgid="6622458130459922952">"Assist apps can help you based on information from the screen you’re viewing. Some apps support both launcher and voice input services to give you integrated assistance."</string>
     <string name="role_browser_label" msgid="2877796144554070207">"Default browser app"</string>
     <string name="role_browser_short_label" msgid="6745009127123292296">"Browser app"</string>
-    <string name="role_browser_description" msgid="3465253637499842671">"Apps that give you access to the Internet and display links that you tap"</string>
+    <string name="role_browser_description" msgid="3465253637499842671">"Apps that give you access to the internet and display links that you tap"</string>
     <string name="role_browser_request_title" msgid="2895200507835937192">"Set <xliff:g id="APP_NAME">%1$s</xliff:g> as your default browser app?"</string>
     <string name="role_browser_request_description" msgid="5888803407905985941">"No permissions needed"</string>
     <string name="role_dialer_label" msgid="1100224146343237968">"Default phone app"</string>
     <string name="role_dialer_short_label" msgid="7186888549465352489">"Phone app"</string>
     <string name="role_dialer_description" msgid="8768708633696539612">"Apps that allow you to make and receive telephone calls on your device"</string>
     <string name="role_dialer_request_title" msgid="5959618560705912058">"Set <xliff:g id="APP_NAME">%1$s</xliff:g> as your default phone app?"</string>
-    <string name="role_dialer_request_description" msgid="6288839625724909320">"This app will be given access to your camera, contacts, microphone, phone and SMS"</string>
-    <string name="role_dialer_search_keywords" msgid="3324448983559188087">"dialler"</string>
+    <string name="role_dialer_request_description" msgid="6288839625724909320">"This app will be given access to your Camera, Contacts, Microphone, Phone, and SMS"</string>
+    <string name="role_dialer_search_keywords" msgid="3324448983559188087">"dialer"</string>
     <string name="role_sms_label" msgid="8456999857547686640">"Default SMS app"</string>
     <string name="role_sms_short_label" msgid="4371444488034692243">"SMS app"</string>
-    <string name="role_sms_description" msgid="3424020199148153513">"Apps that allow you to use your phone number to send and receive short text messages, photos, videos and more"</string>
+    <string name="role_sms_description" msgid="3424020199148153513">"Apps that allow you to use your phone number to send and receive short text messages, photos, videos, and more"</string>
     <string name="role_sms_request_title" msgid="7953552109601185602">"Set <xliff:g id="APP_NAME">%1$s</xliff:g> as your default SMS app?"</string>
-    <string name="role_sms_request_description" msgid="2691004766132144886">"This app will be given access to your camera, contacts, files and media, microphone, phone and SMS"</string>
+    <string name="role_sms_request_description" msgid="2691004766132144886">"This app will be given access to your Camera, Contacts, Files and media, Microphone, Phone, and SMS"</string>
     <string name="role_sms_search_keywords" msgid="8022048144395047352">"text message, texting, messages, messaging"</string>
     <string name="role_emergency_label" msgid="7028825857206842366">"Default emergency app"</string>
     <string name="role_emergency_short_label" msgid="2388431453335350348">"Emergency app"</string>
@@ -381,9 +387,9 @@
     <string name="role_call_redirection_description" msgid="6091669882014664420">"Apps that allow you to forward outgoing calls to another phone number"</string>
     <string name="role_call_redirection_request_title" msgid="2816244455003562925">"Set <xliff:g id="APP_NAME">%1$s</xliff:g> as your default call redirection app?"</string>
     <string name="role_call_redirection_request_description" msgid="3118895714178527164">"No permissions needed"</string>
-    <string name="role_call_screening_label" msgid="883935222060878724">"Default caller ID and spam app"</string>
-    <string name="role_call_screening_short_label" msgid="2048465565063130834">"Caller ID and spam app"</string>
-    <string name="role_call_screening_description" msgid="2349431420497468981">"Apps that allow you to identify calls and block spam, robocalls or unwanted numbers"</string>
+    <string name="role_call_screening_label" msgid="883935222060878724">"Default caller ID &amp; spam app"</string>
+    <string name="role_call_screening_short_label" msgid="2048465565063130834">"Caller ID &amp; spam app"</string>
+    <string name="role_call_screening_description" msgid="2349431420497468981">"Apps that allow you to identify calls and block spam, robocalls, or unwanted numbers"</string>
     <string name="role_call_screening_request_title" msgid="7358309224566977290">"Set <xliff:g id="APP_NAME">%1$s</xliff:g> as your default caller ID &amp; spam app?"</string>
     <string name="role_call_screening_request_description" msgid="7338511921032446006">"No permissions needed"</string>
     <string name="role_automotive_navigation_label" msgid="2701890757955474751">"Default navigation app"</string>
@@ -393,26 +399,26 @@
     <string name="role_automotive_navigation_request_description" msgid="7073023813249245540">"No permissions needed"</string>
     <string name="role_watch_description" msgid="267003778693177779">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions."</string>
     <string name="role_app_streaming_description" msgid="7341638576226183992">"<xliff:g id="APP_NAME">%1$s</xliff:g> will be allowed to interact with your notifications and stream your apps to the connected device."</string>
-    <string name="role_companion_device_computer_description" msgid="416099879217066377">"This service shares your photos, media and notifications from your phone to other devices."</string>
+    <string name="role_companion_device_computer_description" msgid="416099879217066377">"This service shares your photos, media, and notifications from your phone to other devices."</string>
     <string name="request_role_current_default" msgid="738722892438247184">"Current default"</string>
-    <string name="request_role_dont_ask_again" msgid="3556017886029520306">"Don\'t ask again"</string>
+    <string name="request_role_dont_ask_again" msgid="3556017886029520306">"Don’t ask again"</string>
     <string name="request_role_set_as_default" msgid="4253949643984172880">"Set as default"</string>
     <string name="phone_call_uses_microphone" msgid="233569591461187177">"Microphone is used in &lt;b&gt;phone call&lt;/b&gt;"</string>
-    <string name="phone_call_uses_microphone_and_camera" msgid="6291898755681748189">"Camera and microphone are used in &lt;b&gt;video call&lt;/b&gt;"</string>
+    <string name="phone_call_uses_microphone_and_camera" msgid="6291898755681748189">"Camera and Microphone are used in &lt;b&gt;video call&lt;/b&gt;"</string>
     <string name="phone_call_uses_camera" msgid="2048417022147857418">"Camera is used in &lt;b&gt;video call&lt;/b&gt;"</string>
     <string name="system_uses_microphone" msgid="576672130318877143">"Microphone is accessed using system service"</string>
-    <string name="system_uses_microphone_and_camera" msgid="5124478304275138804">"Camera and microphone are accessed using system service"</string>
+    <string name="system_uses_microphone_and_camera" msgid="5124478304275138804">"Camera and Microphone are accessed using system service"</string>
     <string name="system_uses_camera" msgid="1911223105234441470">"Camera is accessed using system service"</string>
     <string name="other_use" msgid="6564855051022776692">"Other use:"</string>
-    <string name="ongoing_usage_dialog_ok" msgid="103556809118460072">"OK"</string>
+    <string name="ongoing_usage_dialog_ok" msgid="103556809118460072">"Got it"</string>
     <string name="ongoing_usage_dialog_title" msgid="683836493556628569">"Recent use of <xliff:g id="TYPES_LIST">%s</xliff:g>"</string>
-    <string name="ongoing_usage_dialog_title_mic" msgid="5966714811125593992">"Recent use of microphone"</string>
+    <string name="ongoing_usage_dialog_title_mic" msgid="5966714811125593992">"Recent use of Microphone"</string>
     <string name="ongoing_usage_dialog_title_camera" msgid="7819329688650711470">"Recent use of Camera"</string>
-    <string name="ongoing_usage_dialog_title_mic_camera" msgid="9079747867228772797">"Recent use of microphone &amp; Camera"</string>
+    <string name="ongoing_usage_dialog_title_mic_camera" msgid="9079747867228772797">"Recent use of Microphone &amp; Camera"</string>
     <string name="ongoing_usage_dialog_separator" msgid="1715181526581520068">", "</string>
     <string name="ongoing_usage_dialog_last_separator" msgid="4170995004748832163">" and "</string>
     <string name="default_app_search_keyword" msgid="8330125736889689743">"default apps"</string>
-    <string name="permgroup_list_microphone_and_camera" msgid="962768198001487969">"Microphone and Camera"</string>
+    <string name="permgroup_list_microphone_and_camera" msgid="962768198001487969">"Microphone &amp; Camera"</string>
     <string name="settings_button" msgid="4414988414732479636">"Settings"</string>
     <string name="default_apps" msgid="5119201969348748639">"Default apps"</string>
     <string name="no_default_apps" msgid="2593466527182950231">"No default apps"</string>
@@ -423,20 +429,20 @@
     <string name="default_app_system_default" msgid="6218386768175513760">"(System default)"</string>
     <string name="default_app_no_apps" msgid="115720991680586885">"No apps"</string>
     <string name="car_default_app_selected" msgid="5416420830430644174">"Selected"</string>
-    <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Selected – <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string>
+    <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Selected - <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string>
     <string name="special_app_access_search_keyword" msgid="8032347212290774210">"special app access"</string>
     <string name="special_app_access" msgid="5019319067120213797">"Special app access"</string>
     <string name="no_special_app_access" msgid="6950277571805106247">"No special app access"</string>
     <string name="special_app_access_no_apps" msgid="4102911722787886970">"No apps"</string>
     <string name="home_missing_work_profile_support" msgid="1756855847669387977">"Doesn’t support work profile"</string>
     <string name="encryption_unaware_confirmation_message" msgid="8274491794636402484">"Note: If you restart your device and have a screen lock set, this app can’t start until you unlock your device."</string>
-    <string name="assistant_confirmation_message" msgid="7476540402884416212">"The assistant will be able to read information about apps that are in use on your system, including information visible on your screen or accessible within the apps."</string>
+    <string name="assistant_confirmation_message" msgid="7476540402884416212">"The assistant will be able to read information about apps in use on your system, including information visible on your screen or accessible within the apps."</string>
     <string name="incident_report_channel_name" msgid="3144954065936288440">"Share Debugging Data"</string>
     <string name="incident_report_notification_title" msgid="4635984625656519773">"Share detailed debugging data?"</string>
     <string name="incident_report_notification_text" msgid="3376480583513587923">"<xliff:g id="APP_NAME">%1$s</xliff:g> would like to upload debugging information."</string>
     <string name="incident_report_dialog_title" msgid="669104389325204095">"Share debugging data?"</string>
     <string name="incident_report_dialog_intro" msgid="5897733669850951832">"The system has detected a problem."</string>
-    <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> would like to upload a bug report from this device taken on <xliff:g id="DATE">%2$s</xliff:g> at <xliff:g id="TIME">%3$s</xliff:g>. Bug reports include personal information about your device or data logged by apps, such as user names, location data, device identifiers and network information. Only share bug reports with people and apps that you trust with this information. Allow <xliff:g id="APP_NAME_1">%4$s</xliff:g> to upload a bug report?"</string>
+    <string name="incident_report_dialog_text" msgid="5675553296891757523">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> is requesting to upload a bug report from this device taken on <xliff:g id="DATE">%2$s</xliff:g> at <xliff:g id="TIME">%3$s</xliff:g>. Bug reports include personal information about your device or logged by apps, for example, user names, location data, device identifiers, and network information. Only share bug reports with people and apps you trust with this information. Allow <xliff:g id="APP_NAME_1">%4$s</xliff:g> to upload a bug report?"</string>
     <string name="incident_report_error_dialog_text" msgid="4189647113387092272">"There was an error processing the bug report for <xliff:g id="APP_NAME">%1$s</xliff:g>. So sharing the detailed debugging data has been denied. Sorry for the interruption."</string>
     <string name="incident_report_dialog_allow_label" msgid="2970242967721155239">"Allow"</string>
     <string name="incident_report_dialog_deny_label" msgid="3535314290677579383">"Deny"</string>
@@ -454,20 +460,21 @@
     <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s location?"</string>
     <string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"This app may want to access your location all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
     <string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Change location access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
-    <string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"This app wants to access your location all the time, even when you’re not using the app. "<annotation id="link">"Allow in Settings."</annotation></string>
-    <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to find, connect to and determine the relative position of nearby devices?"</string>
-    <string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to find, connect to and determine the relative position of nearby devices? "<annotation id="link">"Allow in settings."</annotation></string>
+    <string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"This app wants to access your location all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
+    <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to find, connect to, and determine the relative position of nearby devices?"</string>
+    <string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to find, connect to, and determine the relative position of nearby devices? "<annotation id="link">"Allow in settings."</annotation></string>
     <string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Change <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>’s location access from approximate to precise?"</string>
     <string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s approximate location?"</string>
     <string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Precise"</string>
     <string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Approximate"</string>
     <string name="permgrouprequest_calendar" msgid="1493150855673603806">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your calendar?"</string>
     <string name="permgrouprequest_sms" msgid="5672063688745420991">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send and view SMS messages?"</string>
-    <string name="permgrouprequest_storage" msgid="8717773092518621602">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos, media and files on your device?"</string>
-    <string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;photos, videos, music and audio&lt;/b&gt; on this device?"</string>
-    <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;photos, videos, music, audio and other files&lt;/b&gt; on this device?"</string>
+    <string name="permgrouprequest_storage" msgid="8717773092518621602">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos, media, and files on your device?"</string>
+    <string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;photos, videos, music, and audio&lt;/b&gt; on this device?"</string>
+    <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;photos, videos, music, audio, and other files&lt;/b&gt; on this device?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access music and audio on this device?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and videos on this device?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Grant &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; access to more photos?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"The app will only be able to record audio while you’re using the app"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
@@ -484,10 +491,10 @@
     <string name="permgrouprequest_calllog" msgid="2065327180175371397">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your phone call logs?"</string>
     <string name="permgrouprequest_phone" msgid="1829234136997316752">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to make and manage phone calls?"</string>
     <string name="permgrouprequest_sensors" msgid="4397358316850652235">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access sensor data about your vital signs?"</string>
-    <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"This app wants to access sensor data about your vital signs all the time, even when you’re not using the app. To make this change, "<annotation id="link">"go to Settings."</annotation></string>
+    <string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"This app wants to access sensor data about your vital signs all the time, even when you’re not using the app. To make this change, "<annotation id="link">"go to settings."</annotation></string>
     <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access the sensor data about your vital signs?"</string>
     <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"To let this app access body sensor data all the time, even when you’re not using the app, "<annotation id="link">"go to settings."</annotation></string>
-    <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Keep allowing &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access body sensor data while the app is in use?"</string>
+    <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Keep allowing &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access body sensor data while app is in use?"</string>
     <string name="permgrouprequest_notifications" msgid="6396739062335106181">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send you notifications?"</string>
     <string name="auto_granted_permissions" msgid="6009452264824455892">"Controlled permissions"</string>
     <string name="auto_granted_location_permission_notification_title" msgid="1438871159268985993">"Location can be accessed"</string>
@@ -516,12 +523,12 @@
     <string name="safety_center_rescan_button" msgid="4517514567809409596">"Scan device"</string>
     <string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Dismiss"</string>
     <string name="safety_center_issue_card_dismiss_confirmation_title" msgid="2734809473425036382">"Dismiss this alert?"</string>
-    <string name="safety_center_issue_card_dismiss_confirmation_message" msgid="3775418736671093563">"Review your security and privacy settings at any time to add more protection"</string>
+    <string name="safety_center_issue_card_dismiss_confirmation_message" msgid="3775418736671093563">"Review your security and privacy settings any time to add more protection"</string>
     <string name="safety_center_issue_card_confirm_dismiss_button" msgid="5884137843083634556">"Dismiss"</string>
     <string name="safety_center_issue_card_cancel_dismiss_button" msgid="2874578798877712346">"Cancel"</string>
     <string name="safety_center_entries_category_title" msgid="34356964062813115">"Settings"</string>
     <string name="safety_status_preference_title_and_summary_content_description" msgid="3511373256505058464">"Security and privacy status. <xliff:g id="OVERALL_SAFETY_STATUS">%1$s</xliff:g>. <xliff:g id="SUMMARY_OF_DEVICE_STATUS">%2$s</xliff:g>"</string>
-    <string name="security_settings" msgid="3808106921175271317">"Security settings"</string>
+    <string name="security_settings" msgid="3808106921175271317">"Security Settings"</string>
     <string name="sensor_permissions_qs" msgid="1022267900031317472">"Permissions"</string>
     <string name="safety_privacy_qs_tile_title" msgid="727301867710374052">"Security &amp; privacy"</string>
     <string name="safety_privacy_qs_tile_subtitle" msgid="3621544532041936749">"Check status"</string>
@@ -554,10 +561,10 @@
     <string name="media_confirm_dialog_title_q_to_s_aural_deny" msgid="3128147568953297969">"Access to photos and videos also won’t be allowed"</string>
     <string name="media_confirm_dialog_title_q_to_s_visual_allow" msgid="6310682466493330434">"Access to music and audio files will also be allowed"</string>
     <string name="media_confirm_dialog_title_q_to_s_visual_deny" msgid="1123845663785900471">"Access to music and audio files also won’t be allowed"</string>
-    <string name="media_confirm_dialog_message_a_to_p_aural_allow" msgid="7865167246140107623">"This app doesn’t support the latest version of Android. If this app can access music and audio files, it will also be allowed to access photos, videos and other files."</string>
-    <string name="media_confirm_dialog_message_a_to_p_aural_deny" msgid="287502523664804786">"This app doesn’t support the latest version of Android. If this app can’t access music and audio files, it also won’t be allowed to access photos, videos and other files."</string>
-    <string name="media_confirm_dialog_message_a_to_p_visual_allow" msgid="4952410892939590487">"This app doesn’t support the latest version of Android. If this app can access photos and videos, it will also be allowed to access music, audio and other files."</string>
-    <string name="media_confirm_dialog_message_a_to_p_visual_deny" msgid="6609500525590757681">"This app doesn’t support the latest version of Android. If this app can’t access photos and videos, it also won’t be allowed to access music, audio and other files."</string>
+    <string name="media_confirm_dialog_message_a_to_p_aural_allow" msgid="7865167246140107623">"This app doesn’t support the latest version of Android. If this app can access music and audio files, it will also be allowed to access photos, videos, and other files."</string>
+    <string name="media_confirm_dialog_message_a_to_p_aural_deny" msgid="287502523664804786">"This app doesn’t support the latest version of Android. If this app can’t access music and audio files, it also won’t be allowed to access photos, videos, and other files."</string>
+    <string name="media_confirm_dialog_message_a_to_p_visual_allow" msgid="4952410892939590487">"This app doesn’t support the latest version of Android. If this app can access photos and videos, it will also be allowed to access music, audio, and other files."</string>
+    <string name="media_confirm_dialog_message_a_to_p_visual_deny" msgid="6609500525590757681">"This app doesn’t support the latest version of Android. If this app can’t access photos and videos, it also won’t be allowed to access music, audio, other files."</string>
     <string name="media_confirm_dialog_message_q_to_s_aural_allow" msgid="1702402580147536160">"This app doesn’t support the latest version of Android. If this app can access music and audio files, it will also be allowed to access photos and videos."</string>
     <string name="media_confirm_dialog_message_q_to_s_aural_deny" msgid="6832087393653561911">"This app doesn’t support the latest version of Android. If this app can’t access music and audio files, it also won’t be allowed to access photos and videos."</string>
     <string name="media_confirm_dialog_message_q_to_s_visual_allow" msgid="3504335060843147760">"This app doesn’t support the latest version of Android. If this app can access photos and videos, it will also be allowed to access music and audio files."</string>
@@ -575,7 +582,8 @@
     <string name="mic_toggle_description" msgid="9163104307990677157">"For apps and services. If this setting is off, microphone data may still be shared when you call an emergency number."</string>
     <string name="location_settings_subtitle" msgid="2328360561197430695">"See apps and services that have access to location"</string>
     <string name="show_clip_access_notification_title" msgid="5168467637351109096">"Show clipboard access"</string>
-    <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Show a message when apps access text, images or other content that you’ve copied"</string>
+    <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Show a message when apps access text, images, or other content you’ve copied"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Show passwords"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Display characters briefly as you type"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"This app stated it may share <xliff:g id="PERMISSION_NAME">%s</xliff:g> data with third parties"</string>
 </resources>
diff --git a/PermissionController/res/values-en-rGB/strings.xml b/PermissionController/res/values-en-rGB/strings.xml
index 61064f8..f6091d7 100644
--- a/PermissionController/res/values-en-rGB/strings.xml
+++ b/PermissionController/res/values-en-rGB/strings.xml
@@ -28,10 +28,14 @@
     <string name="uninstall_or_disable" msgid="4496612999740858933">"Uninstall or disable"</string>
     <string name="app_not_found_dlg_title" msgid="6029482906093859756">"App not found"</string>
     <string name="grant_dialog_button_deny" msgid="88262611492697192">"Don\'t allow"</string>
-    <string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Don’t allow &amp; don’t ask again"</string>
+    <string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Don’t allow and don’t ask again"</string>
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Keep \'While the app is in use\'"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Keep \'Only this time\'"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"More info"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Allow access to all photos"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Select photos"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Select more photos"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Don’t select more photos"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Don’t allow anyway"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Dismiss"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> of <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Allow access to media only"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Allow all the time"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Allow only while using the app"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Allow all photos"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Allow selected photos"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Ask every time"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Don\'t allow"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Precise location"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;photos, videos, music, audio and other files&lt;/b&gt; on this device?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access music and audio on this device?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and videos on this device?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Grant &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; access to more photos?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"The app will only be able to record audio while you’re using the app"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Show a message when apps access text, images or other content that you’ve copied"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Show passwords"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Display characters briefly as you type"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"This app stated that it may share <xliff:g id="PERMISSION_NAME">%s</xliff:g> data with third parties"</string>
 </resources>
diff --git a/PermissionController/res/values-en-rIN/strings.xml b/PermissionController/res/values-en-rIN/strings.xml
index 61064f8..f6091d7 100644
--- a/PermissionController/res/values-en-rIN/strings.xml
+++ b/PermissionController/res/values-en-rIN/strings.xml
@@ -28,10 +28,14 @@
     <string name="uninstall_or_disable" msgid="4496612999740858933">"Uninstall or disable"</string>
     <string name="app_not_found_dlg_title" msgid="6029482906093859756">"App not found"</string>
     <string name="grant_dialog_button_deny" msgid="88262611492697192">"Don\'t allow"</string>
-    <string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Don’t allow &amp; don’t ask again"</string>
+    <string name="grant_dialog_button_deny_and_dont_ask_again" msgid="1748925431574312595">"Don’t allow and don’t ask again"</string>
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Keep \'While the app is in use\'"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Keep \'Only this time\'"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"More info"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Allow access to all photos"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Select photos"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Select more photos"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Don’t select more photos"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Don’t allow anyway"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Dismiss"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> of <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Allow access to media only"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Allow all the time"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Allow only while using the app"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Allow all photos"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Allow selected photos"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Ask every time"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Don\'t allow"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Precise location"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;photos, videos, music, audio and other files&lt;/b&gt; on this device?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access music and audio on this device?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and videos on this device?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Grant &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; access to more photos?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"The app will only be able to record audio while you’re using the app"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Show a message when apps access text, images or other content that you’ve copied"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Show passwords"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Display characters briefly as you type"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"This app stated that it may share <xliff:g id="PERMISSION_NAME">%s</xliff:g> data with third parties"</string>
 </resources>
diff --git a/PermissionController/res/values-en-rXC/strings.xml b/PermissionController/res/values-en-rXC/strings.xml
index 59ad100..0ee2787 100644
--- a/PermissionController/res/values-en-rXC/strings.xml
+++ b/PermissionController/res/values-en-rXC/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‏‎‎‏‏‏‎‏‏‎‎‎‏‎Keep “While the app is in use”‎‏‎‎‏‎"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‎‎‏‎‏‏‎‏‎‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‎‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‎‎Keep “Only this time”‎‏‎‎‏‎"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‎‏‎‎‏‎More info‎‏‎‎‏‎"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‎‎‎‎‏‏‎‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‏‎‎‎‏‎‎‎Allow access to all photos‎‏‎‎‏‎"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‏‎‎‎‎‏‎‎‏‏‏‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎Select photos‎‏‎‎‏‎"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‎‏‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‏‎Select more photos‎‏‎‎‏‎"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‏‎‎‎‎‏‏‎‏‏‏‏‎‎‏‎‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎‎Don’t select more photos‎‏‎‎‏‎"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‎‎‏‏‏‎‎‏‏‏‏‎Don’t allow anyway‎‏‎‎‏‎"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‏‎‏‎‎‎‏‎‏‎‎‎‎‏‎‏‎‏‏‎‎‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‎‏‎‎‏‎‏‏‎‏‏‎‎‏‎Dismiss‎‏‎‎‏‎"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‏‏‏‎‎‏‎‏‎‏‏‎‏‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>‎‏‎‎‏‏‏‎ of ‎‏‎‎‏‏‎<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‎‏‎‏‎‏‏‎‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‏‎‏‏‎‏‎‏‏‎‎‏‎‏‎‎Allow access to media only‎‏‎‎‏‎"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‎‎‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‏‏‎‏‎‏‏‎‎‏‏‏‎‏‎‎‏‎‎‎‏‏‎Allow all the time‎‏‎‎‏‎"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‎‏‎‎‎‏‏‎‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‏‎Allow only while using the app‎‏‎‎‏‎"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‎‏‏‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎Allow all photos‎‏‎‎‏‎"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‎‏‎‎‎‏‎‏‏‎‏‏‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‎‏‏‏‏‎‏‎‎‏‎‏‎‏‎‎‎Allow selected photos‎‏‎‎‏‎"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‎‎‏‎‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‏‎‏‎‏‎‏‏‎‏‎‏‎‎‎‎‏‎‎‏‏‎‏‎‎‎‎‎‎‏‏‎‎‏‏‎Ask every time‎‏‎‎‏‎"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‎‏‏‏‎‎‏‏‎‎‏‏‏‏‏‎‎‎Don’t allow‎‏‎‎‏‎"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‎‎‎Precise location‎‏‎‎‏‎"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‏‎‏‎‏‎‏‎‏‎‏‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‏‏‏‏‎‎‎‎‏‎‎‏‎‏‎‏‎‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access &lt;b&gt;photos, videos, music, audio, and other files&lt;/b&gt; on this device?‎‏‎‎‏‎"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‎‎‎‏‏‎‏‏‎‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access music and audio on this device?‎‏‎‎‏‎"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‎‏‏‎‏‎‎‎‎‏‏‎‏‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access photos and videos on this device?‎‏‎‎‏‎"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‎‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‎Grant &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; access to more photos?‎‏‎‎‏‎"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‎‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‎‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to record audio?‎‏‎‎‏‎"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‏‎‎‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‏‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎The app will only be able to record audio while you’re using the app‎‏‎‎‏‎"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‎‏‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to record audio?‎‏‎‎‏‎"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‎‎‎‎‎‏‏‎‏‎‎‏‎‎‏‎‎‎‏‏‏‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‏‏‏‎‏‎‏‏‏‏‎Show a message when apps access text, images, or other content you’ve copied‎‏‎‎‏‎"</string>
     <string name="show_password_title" msgid="2877269286984684659">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎‎‏‏‎‏‏‏‎‏‏‏‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎‎‏‏‎Show passwords‎‏‎‎‏‎"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎‎‏‎‎‎‏‎‎‏‎‏‎‏‎‏‎‎Display characters briefly as you type‎‏‎‎‏‎"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‎‏‎‎‏‎‏‏‏‎‎‎‎‏‏‎This app stated it may share ‎‏‎‎‏‏‎<xliff:g id="PERMISSION_NAME">%s</xliff:g>‎‏‎‎‏‏‏‎ data with third parties‎‏‎‎‏‎"</string>
 </resources>
diff --git a/PermissionController/res/values-es-rUS/strings.xml b/PermissionController/res/values-es-rUS/strings.xml
index 5821f07..21ff83a 100644
--- a/PermissionController/res/values-es-rUS/strings.xml
+++ b/PermissionController/res/values-es-rUS/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Mantener en \"Mientras la app está en uso\""</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Mantener \"Solo esta vez\""</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Más información"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Permitir el acceso a todas las fotos"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Seleccionar fotos"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Seleccionar más fotos"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"No seleccionar más fotos"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"No permitir igualmente"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Ignorar"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Solo permitir acceso al contenido multimedia"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Permitir todo el tiempo"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Permitir solo con la app en uso"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Permitir todas las fotos"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Permitir las fotos seleccionadas"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Preguntar siempre"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"No permitir"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Ubicación precisa"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a &lt;b&gt;fotos, videos, música, audio y otros archivos&lt;/b&gt; del dispositivo?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la música y los archivos de audio de este dispositivo?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a las fotos y los videos de este dispositivo?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"¿Quieres que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pueda acceder a más fotos?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grabe audio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"La app solo podrá grabar audio cuando esté en uso"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grabe audio?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Mostrar un mensaje cuando las apps accedan a textos, imágenes y otro contenido que hayas copiado"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Mostrar contraseñas"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Mostrar caracteres brevemente mientras escribes"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Esta app indicó que podría compartir datos de <xliff:g id="PERMISSION_NAME">%s</xliff:g> con terceros"</string>
 </resources>
diff --git a/PermissionController/res/values-es/strings.xml b/PermissionController/res/values-es/strings.xml
index 363eb7a..32f8ca1 100644
--- a/PermissionController/res/values-es/strings.xml
+++ b/PermissionController/res/values-es/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Mantener \"Mientras la aplicación se esté usando\""</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Mantener \"Solo esta vez\""</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Más información"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Permitir acceso a todas las fotos"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Seleccionar fotos"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Seleccionar más fotos"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"No seleccionar más fotos"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"No permitir de todas formas"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Cerrar"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Permitir acceso solo al contenido multimedia"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Permitir siempre"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Permitir solo mientras se usa la aplicación"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Permitir todas la fotos"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Permitir fotos seleccionadas"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Preguntar siempre"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"No permitir"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Ubicación precisa"</string>
@@ -244,7 +250,7 @@
     <string name="app_permission_never_accessed_denied_summary" msgid="6596000497490905146">"Denegado / Último acceso: Nunca"</string>
     <string name="allowed_header" msgid="7769277978004790414">"Permitido"</string>
     <string name="allowed_always_header" msgid="6455903312589013545">"Permitido siempre"</string>
-    <string name="allowed_foreground_header" msgid="6845655788447833353">"Permitido solo mientras se usa"</string>
+    <string name="allowed_foreground_header" msgid="6845655788447833353">"Permitidas solo mientras se usan"</string>
     <string name="allowed_storage_scoped" msgid="5383645873719086975">"Pueden acceder solo al contenido multimedia"</string>
     <string name="allowed_storage_full" msgid="5356699280625693530">"Pueden gestionar todos los archivos"</string>
     <string name="ask_header" msgid="2633816846459944376">"Preguntar siempre"</string>
@@ -281,7 +287,7 @@
     <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"Esta aplicación puede acceder siempre a tu ubicación. Toca para cambiarlo."</string>
     <string name="notification_listener_reminder_notification_title" msgid="3747210460187479091">"Revisa la aplicación con acceso a tus notificaciones"</string>
     <string name="notification_listener_reminder_notification_content" msgid="831476101108863427">"<xliff:g id="APP_NAME">%s</xliff:g> puede descartar, acceder o interactuar con el contenido de tus notificaciones"</string>
-    <string name="notification_listener_warning_card_content" msgid="7840973324284115893">"Esta aplicación puede descartar, actuar o acceder al contenido de tus notificaciones. Algunas aplicaciones requieren este acceso para funcionar según lo previsto."</string>
+    <string name="notification_listener_warning_card_content" msgid="7840973324284115893">"Esta aplicación puede descartar, actuar de acuerdo a o acceder al contenido de tus notificaciones. Algunas aplicaciones requieren este acceso para funcionar según lo previsto."</string>
     <string name="notification_listener_remove_access_button_label" msgid="7101898782417817097">"Quitar acceso"</string>
     <string name="notification_listener_review_app_button_label" msgid="3433073281029143924">"Ver más opciones"</string>
     <string name="notification_listener_remove_access_success_label" msgid="2477611529875633107">"Acceso retirado"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a &lt;b&gt;fotos, vídeos, música, audio y otros archivos&lt;/b&gt; del dispositivo?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a música y audio de este dispositivo?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a fotos y vídeos de este dispositivo?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a más fotos?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grabe audio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"La aplicación solo podrá grabar audio mientras la estés usando."</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grabe audio?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Muestra un mensaje cuando las aplicaciones acceden a texto, imágenes u otro contenido que has copiado"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Mostrar contraseñas"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Muestra los caracteres brevemente mientras escribes"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Esta aplicación ha indicado que es posible que comparta datos de <xliff:g id="PERMISSION_NAME">%s</xliff:g> con terceros"</string>
 </resources>
diff --git a/PermissionController/res/values-et/strings.xml b/PermissionController/res/values-et/strings.xml
index 9538ffa..0fcb636 100644
--- a/PermissionController/res/values-et/strings.xml
+++ b/PermissionController/res/values-et/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Säilita valik „Rakenduse kasutamise ajal”"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Säilita ainult sel korral"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Lisateave"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Luba juurdepääs kõikidele fotodele"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Vali fotod"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Valige veel fotosid"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Ära vali rohkem fotosid"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ära luba ikkagi"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Loobu"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Luba juurdepääs ainult meediale"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Luba alati"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Luba rakenduse kasutamise ajal"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Luba kõik fotod"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Luba valitud fotod"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Küsi iga kord"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Ära luba"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Täpne asukoht"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Anda rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdep. &lt;b&gt;foto-, video-, muusika-, heli- ja muudele failidele&lt;/b&gt;?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Kas anda rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; selles seadmes juurdepääs muusikale ja helifailidele?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Kas anda rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; selles seadmes juurdepääs fotodele ja videotele?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Kas anda rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs rohkematele fotodele?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; salvestada heli?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Rakendus saab heli salvestada vaid siis, kui rakendust kasutate"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; heli salvestada?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Kui rakendused pääsevad juurde kopeeritud tekstile, piltidele või muule sisule, kuvatakse teade"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Kuva paroolid"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Sisestamisel kuvatakse hetkeks tähemärgid"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"See rakendus andis teada, et võib loa <xliff:g id="PERMISSION_NAME">%s</xliff:g> andmeid jagada kolmandate osapooltega"</string>
 </resources>
diff --git a/PermissionController/res/values-eu/strings.xml b/PermissionController/res/values-eu/strings.xml
index cd993e6..4b8abdb 100644
--- a/PermissionController/res/values-eu/strings.xml
+++ b/PermissionController/res/values-eu/strings.xml
@@ -32,6 +32,14 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Mantendu “Aplikazioa abian denean” aukera"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Mantendu \"Oraingo honetan soilik\""</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Datu gehiago"</string>
+    <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) -->
+    <skip />
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ez eman baimenik halere"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Baztertu"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +194,10 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Eman multimedia-fitxategiak soilik atzitzeko baimena"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Eman baimena beti"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Aplikazioa erabiltzean soilik"</string>
+    <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) -->
+    <skip />
+    <!-- no translation found for app_permission_button_select_photos (1022930616634145364) -->
+    <skip />
     <string name="app_permission_button_ask" msgid="3342950658789427">"Galdetu beti"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Ez eman baimenik"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Kokapen zehatza"</string>
@@ -468,6 +480,8 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Gailuko &lt;b&gt;argazkiak, bideoak, musika, audioa eta bestelako fitxategiak&lt;/b&gt; atzitzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Gailuko musika eta audioa atzitzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Gailuko argazkiak eta bideoak atzitzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
+    <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) -->
+    <skip />
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Audioa grabatzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikazioak hura erabiltzean soilik grabatuko du audioa"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Audioa grabatzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
@@ -578,4 +592,6 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Erakutsi mezu bat aplikazio batek kopiatu dituzun testuak, irudiak edo edukiak atzitzen dituenean"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Erakutsi pasahitzak"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Idatzi ahala, erakutsi karaktereak laburki"</string>
+    <!-- no translation found for permission_rationale_message_template (4497650516269082051) -->
+    <skip />
 </resources>
diff --git a/PermissionController/res/values-fa/strings.xml b/PermissionController/res/values-fa/strings.xml
index 3741730..9e1d0c7 100644
--- a/PermissionController/res/values-fa/strings.xml
+++ b/PermissionController/res/values-fa/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"تغییر ندادن اجازه «هنگامی که از برنامه استفاده می‌شود»"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"«فقط این بار» نگه داشته شود"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"اطلاعات بیشتر"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"مجاز کردن دسترسی به همه عکس‌ها"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"انتخاب عکس مجاز است"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"انتخاب عکس‌های بیشتر مجاز است"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"انتخاب عکس‌های بیشتر مجاز نیست"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"درهرصورت اجازه نیست"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"رد کردن"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> مجوز از <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> مجوز"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"اجازه دادن فقط برای دسترسی به رسانه‌ها"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"همیشه مجاز است"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"فقط هنگام استفاده از برنامه مجاز است"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"مجاز کردن همه عکس‌ها"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"مجاز کردن عکس‌های منتخب"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"هربار پرسیده شود"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"اجازه ندادن"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"مکان دقیق"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه می‌دهید به &lt;b&gt;عکس‌ها، ویدیوها، موسیقی، صوت، و فایل‌های دیگر&lt;/b&gt; این دستگاه دسترسی داشته باشد؟"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه می‌دهید به فایل‌های موسیقی و صوتی در این دستگاه دسترسی داشته باشد؟"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه می‌دهید به عکس‌ها و ویدیوهای این دستگاه دسترسی داشته باشد؟"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه می‌دهید به عکس‌های بیشتری دسترسی داشته باشد؟"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"‏به &lt;/b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود صدا ضبط کند؟"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"این برنامه فقط وقتی از آن استفاده می‌کنید، می‌تواند صدا ضبط کند"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"‏به &lt;/b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود صدا ضبط کند؟"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"وقتی برنامه‌ها به نوشتار، تصویر، یا محتوای دیگری که کپی کرده‌اید دسترسی پیدا می‌کنند، پیامی نشان داده می‌شود"</string>
     <string name="show_password_title" msgid="2877269286984684659">"نمایش گذرواژه‌ها"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"همان‌طور که تایپ می‌کنید، نویسه‌ها را برای مدت کوتاهی نشان می‌دهد"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"این برنامه بیان کرده است که ممکن است داده‌های <xliff:g id="PERMISSION_NAME">%s</xliff:g> را با اشخاص ثالث هم‌رسانی کند"</string>
 </resources>
diff --git a/PermissionController/res/values-fi/strings.xml b/PermissionController/res/values-fi/strings.xml
index f75b1e2..73cb8e9 100644
--- a/PermissionController/res/values-fi/strings.xml
+++ b/PermissionController/res/values-fi/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Pidä \"Kun sovellusta käytetään\""</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Säilytä \"Vain tällä kertaa\""</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Lisätietoja"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Salli pääsy kaikkiin kuviin"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Valitse kuvat"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Valitse lisää kuvia"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Älä valitse enempää kuvia"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Älä salli silti"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Ohita"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Salli pääsy vain mediaan"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Salli aina"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Salli vain, kun sovellus on käytössä"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Salli kaikki kuvat"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Salli valitut kuvat"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Kysy aina"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Älä salli"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Tarkka sijainti"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn &lt;b&gt;kuviin, videoihin, musiikkiin, audioon ja muihin tiedostoihin&lt;/b&gt;?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn tällä laitteella oleviin musiikki- ja audiotiedostoihin?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn laitteella oleviin kuviin ja mediaan?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn useampiin kuviin?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nauhoittaa audiota?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Sovellus voi tallentaa audiota vain silloin, kun käytät sitä"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tallentaa audiota?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Näytä viesti, kun sovellukset käyttävät kopioimaasi tekstiä, kuvia tai muuta sisältöä"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Näytä salasanat"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Näytä kirjaimet hetkellisesti, kun kirjoitat"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Sovellus on ilmoittanut, että se saattaa jakaa <xliff:g id="PERMISSION_NAME">%s</xliff:g>dataa kolmansille osapuolille"</string>
 </resources>
diff --git a/PermissionController/res/values-fr-rCA/strings.xml b/PermissionController/res/values-fr-rCA/strings.xml
index 2e39398..01b8dc1 100644
--- a/PermissionController/res/values-fr-rCA/strings.xml
+++ b/PermissionController/res/values-fr-rCA/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Garder « Pendant l\'utilisation de l\'appli »"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Garder « Uniquement cette fois »"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"En savoir plus"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Autoriser l\'accès à toutes les photos"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Sélectionner des photos"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Sélectionner plus de photos"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Ne pas sélectionner plus de photos"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ne pas autoriser quand même"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Fermer"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> sur <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Autoriser à accéder aux éléments multimédias seulement"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Toujours autoriser"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Autoriser uniquement lorsque l\'appli est en cours d\'utilisation"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Autoriser toutes les photos"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Autoriser les photos sélectionnées"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Toujours demander"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Ne pas autoriser"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Position exacte"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder &lt;b&gt;aux photos, aux vidéos, et aux fichiers musicaux, audio et autres&lt;/b&gt; sur cet appareil?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux fichiers musicaux et audio sur cet appareil?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux photos et aux vidéos sur cet appareil?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Autoriser l\'accès à plus de photos à &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à enregistrer l\'audio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"L\'application pourra uniquement enregistrer de l\'audio lorsque vous l\'utilisez"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à enregistrer de l\'audio?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Afficher un message lorsque les applications accèdent à du texte, à des images ou à d\'autres contenus que vous avez copiés"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Afficher les mots de passe"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Afficher les caractères brièvement pendant la saisie"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Cette application a indiqué qu\'elle pourrait partager des données de <xliff:g id="PERMISSION_NAME">%s</xliff:g> avec des tiers."</string>
 </resources>
diff --git a/PermissionController/res/values-fr/strings.xml b/PermissionController/res/values-fr/strings.xml
index 8a2bf8f..03609e6 100644
--- a/PermissionController/res/values-fr/strings.xml
+++ b/PermissionController/res/values-fr/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Garder \"Seulement quand l\'appli est en cours d\'utilisation\""</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Conserver le paramètre \"Uniquement cette fois-ci\""</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Plus d\'infos"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Autoriser l\'accès à toutes les photos"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Sélectionner des photos"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Sélectionner d\'autres photos"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Ne pas sélectionner d\'autres photos"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ne pas autoriser"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Fermer"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> sur <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Autoriser l\'accès aux fichiers multimédias uniquement"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Toujours autoriser"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Autoriser seulement si l\'appli est en cours d\'utilisation"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Autoriser toutes les photos"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Autoriser les photos sélectionnées"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Toujours demander"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Ne pas autoriser"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Position exacte"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux &lt;b&gt;photos, vidéos, fichiers musicaux/audio, etc.&lt;/b&gt; sur l\'appareil ?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la musique et à l\'audio sur cet appareil ?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux photos et vidéos sur cet appareil ?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à plus de photos ?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à enregistrer de l\'audio ?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Cette application ne pourra réaliser des enregistrements audio que lorsque vous l\'utiliserez"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Permettre à &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; de réaliser des enregistrements audio ?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Afficher un message lorsque les applis accèdent à du texte, à des images ou à d\'autres contenus que vous avez copiés"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Afficher les mots de passe"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Afficher brièvement les caractères pendant la saisie"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Cette appli a indiqué qu\'elle peut partager des données de <xliff:g id="PERMISSION_NAME">%s</xliff:g> avec des tiers"</string>
 </resources>
diff --git a/PermissionController/res/values-gl/strings.xml b/PermissionController/res/values-gl/strings.xml
index 960f901..14afe1f 100644
--- a/PermissionController/res/values-gl/strings.xml
+++ b/PermissionController/res/values-gl/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Manter mentres se estea utilizando a aplicación"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Manter Só esta vez"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Máis datos"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Permitir acceso a todas as fotos"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Seleccionar fotos"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Seleccionar máis fotos"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Non seleccionar máis fotos"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Non permitir aínda así"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Pechar"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Permitir acceso só a ficheiros multimedia"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Permitir sempre"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Permitir só mentres se use a aplicación"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Permitir todas as fotos"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Permitir fotos seleccionadas"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Preguntar sempre"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Non permitir"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Localización precisa"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda ás &lt;b&gt;fotos, vídeos, música, audio e outros ficheiros&lt;/b&gt; do dispositivo?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda á música e aos ficheiros de audio deste dispositivo?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda ás fotos e aos vídeos deste dispositivo?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a máis fotos?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave audio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Esta aplicación só poderá gravar audio cando a esteas utilizando"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave audio?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Mostra unha mensaxe cando as aplicacións acceden ao texto, ás imaxes ou ao contido que copiaches"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Mostrar contrasinais"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Mostra os caracteres brevemente mentres escribes"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"A aplicación indicou que é posible que comparta con terceiros os seguintes datos: <xliff:g id="PERMISSION_NAME">%s</xliff:g>"</string>
 </resources>
diff --git a/PermissionController/res/values-gu/strings.xml b/PermissionController/res/values-gu/strings.xml
index 9e9f429..1b55eef 100644
--- a/PermissionController/res/values-gu/strings.xml
+++ b/PermissionController/res/values-gu/strings.xml
@@ -32,6 +32,14 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“ઍપ ઉપયોગમાં હોય ત્યારે” આ રાખો"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“ફક્ત આ વખતે” રાખો"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"વધુ માહિતી"</string>
+    <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) -->
+    <skip />
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"કોઈપણ રીતે મંજૂરી આપશો નહીં"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"છોડી દો"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> માંથી <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
@@ -186,6 +194,10 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"માત્ર મીડિયાના ઍક્સેસની મંજૂરી આપો"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"હંમેશાં મંજૂરી આપો"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"ઍપનો ઉપયોગ કરતી વખતે જ મંજૂરી આપો"</string>
+    <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) -->
+    <skip />
+    <!-- no translation found for app_permission_button_select_photos (1022930616634145364) -->
+    <skip />
     <string name="app_permission_button_ask" msgid="3342950658789427">"દર વખતે પૂછો"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"મંજૂરી આપશો નહીં"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"ચોક્કસ સ્થાન"</string>
@@ -468,6 +480,8 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને આ ડિવાઇસ પર &lt;b&gt;ફોટા, વીડિયો, મ્યુઝિક, ઑડિયો અને અન્ય ફાઇલો&lt;b&gt;ના ઍક્સેસની મંજૂરી આપીએ?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"શું &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને આ ડિવાઇસ પરની મ્યુઝિક અને ઑડિયો ફાઇલો ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"શું &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને આ ડિવાઇસ પરના ફોટા અને વીડિયો ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
+    <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) -->
+    <skip />
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને ઑડિયો રેકૉર્ડ કરવાની મંજૂરી આપીએ?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"જ્યારે તમે ઍપનો ઉપયોગ કરી રહ્યા હશો, માત્ર ત્યારે જ ઍપ ઑડિયો રેકોર્ડ કરી શકશે"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને ઑડિયો રેકોર્ડ કરવાની મંજૂરી આપીએ?"</string>
@@ -578,4 +592,6 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"જ્યારે ઍપ તમે કૉપિ કરેલી ટેક્સ્ટ, છબીઓ કે અન્ય કન્ટેન્ટનો ઍક્સેસ કરે, ત્યારે મેસેજ બતાવો"</string>
     <string name="show_password_title" msgid="2877269286984684659">"પાસવર્ડ બતાવો"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"તમે ટાઇપ કરો ત્યારે થોડા સમય માટે અક્ષરો બતાવો"</string>
+    <!-- no translation found for permission_rationale_message_template (4497650516269082051) -->
+    <skip />
 </resources>
diff --git a/PermissionController/res/values-hi/strings.xml b/PermissionController/res/values-hi/strings.xml
index 700637e..815001b 100644
--- a/PermissionController/res/values-hi/strings.xml
+++ b/PermissionController/res/values-hi/strings.xml
@@ -32,6 +32,14 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"\"ऐप्लिकेशन इस्तेमाल करते समय\" अनुमति बनाए रखें"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“सिर्फ़ इस बार अनुमति दें” को बनाए रखें"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"ज़्यादा जानकारी"</string>
+    <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) -->
+    <skip />
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"फिर भी अनुमति न दें"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"खारिज करें"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> में से <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
@@ -186,6 +194,10 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"सिर्फ़ मीडिया फ़ाइलें ऐक्सेस करने की अनुमति दें"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"हमेशा के लिए अनुमति दें"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"सिर्फ़ ऐप्लिकेशन इस्तेमाल करते समय अनुमति दें"</string>
+    <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) -->
+    <skip />
+    <!-- no translation found for app_permission_button_select_photos (1022930616634145364) -->
+    <skip />
     <string name="app_permission_button_ask" msgid="3342950658789427">"हर बार पूछें"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"अनुमति न दें"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"सटीक जगह"</string>
@@ -279,9 +291,9 @@
     <string name="auto_revoke_preference_summary" msgid="5517958331781391481">"आपकी निजता की सुरक्षा के लिए अनुमतियां हटाई गई हैं"</string>
     <string name="background_location_access_reminder_notification_title" msgid="1140797924301941262">"<xliff:g id="APP_NAME">%s</xliff:g> अब बैकग्राउंड में आपकी जगह की जानकारी ऐक्सेस कर सकता है"</string>
     <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"यह ऐप्लिकेशन हमेशा आपकी जगह की जानकारी ऐक्सेस कर सकता है. बदलने के लिए टैप करें."</string>
-    <string name="notification_listener_reminder_notification_title" msgid="3747210460187479091">"ऐसे ऐप्लिकेशन देखें जिनके पास सूचनाओं का ऐक्सेस है"</string>
-    <string name="notification_listener_reminder_notification_content" msgid="831476101108863427">"<xliff:g id="APP_NAME">%s</xliff:g> आपकी सूचनाओं में मौजूद कॉन्टेंट को खारिज कर सकता है. साथ ही, उस पर कार्रवाई कर सकता है और उसे ऐक्सेस भी कर सकता है"</string>
-    <string name="notification_listener_warning_card_content" msgid="7840973324284115893">"यह ऐप्लिकेशन आपकी सूचनाओं में मौजूद कॉन्टेंट को खारिज कर सकता है. साथ ही, उस पर कार्रवाई कर सकता है और उसे ऐक्सेस भी कर सकता है. कुछ ऐप्लिकेशन को सही तरीके से काम करने के लिए, इस तरह के ऐक्सेस की ज़रूरत होती है."</string>
+    <string name="notification_listener_reminder_notification_title" msgid="3747210460187479091">"ऐसे ऐप्लिकेशन देखें जिनके पास आपकी सूचनाओं का ऐक्सेस है"</string>
+    <string name="notification_listener_reminder_notification_content" msgid="831476101108863427">"<xliff:g id="APP_NAME">%s</xliff:g> आपकी सूचनाओं में मौजूद कॉन्टेंट को ऐक्सेस कर सकता है. साथ ही, उस पर कार्रवाई कर सकता है और उसे खारिज कर सकता है."</string>
+    <string name="notification_listener_warning_card_content" msgid="7840973324284115893">"यह ऐप्लिकेशन आपकी सूचनाओं में मौजूद कॉन्टेंट को ऐक्सेस कर सकता है, उस पर कार्रवाई कर सकता है, और उसे खारिज कर सकता है. कुछ ऐप्लिकेशन को सही तरीके से काम करने के लिए, इस तरह के ऐक्सेस की ज़रूरत होती है."</string>
     <string name="notification_listener_remove_access_button_label" msgid="7101898782417817097">"ऐक्सेस हटाएं"</string>
     <string name="notification_listener_review_app_button_label" msgid="3433073281029143924">"ज़्यादा विकल्प देखें"</string>
     <string name="notification_listener_remove_access_success_label" msgid="2477611529875633107">"ऐक्सेस हटाया गया"</string>
@@ -468,6 +480,8 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को डिवाइस में मौजूद &lt;b&gt;फ़ोटो, वीडियो, संगीत, ऑडियो, और अन्य फ़ाइल&lt;/b&gt; का ऐक्सेस देना है?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को इस डिवाइस में मौजूद संगीत और ऑडियो ऐक्सेस करने की अनुमति देनी है?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को इस डिवाइस में मौजूद फ़ोटो और वीडियो ऐक्सेस करने की अनुमति देनी है?"</string>
+    <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) -->
+    <skip />
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को ऑडियो रिकॉर्ड करने की अनुमति देनी है?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ऐप्लिकेशन सिर्फ़ तब ही ऑडियो रिकॉर्ड कर पाएगा, जब आप ऐप्लिकेशन इस्तेमाल कर रहे हों"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"क्या आप &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को ऑडियो रिकॉर्ड करने की अनुमति देना चाहते हैं?"</string>
@@ -488,7 +502,7 @@
     <string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को अपने शरीर के बारे में जानकारी देने वाले लक्षणों के सेंसर डेटा को ऐक्सेस करने की अनुमति दें?"</string>
     <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"इस ऐप्लिकेशन का इस्तेमाल न किए जाने पर भी, इसे बॉडी सेंसर के डेटा को हमेशा ऐक्सेस करने की अनुमति देने के लिए, "<annotation id="link">"सेटिंग पर जाएं."</annotation></string>
     <string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"क्या इस्तेमाल के दौरान, &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को बॉडी सेंसर के डेटा का ऐक्सेस देते रहना है?"</string>
-    <string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को, आपको सूचनाएं भेजने की अनुमति देनी है?"</string>
+    <string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को सूचनाएं भेजने की अनुमति दें?"</string>
     <string name="auto_granted_permissions" msgid="6009452264824455892">"कंट्रोल की गई अनुमतियां"</string>
     <string name="auto_granted_location_permission_notification_title" msgid="1438871159268985993">"जगह की जानकारी देखी जा सकती है"</string>
     <string name="auto_granted_permission_notification_body" msgid="6919835973190443695">"आपका आईटी एडमिन <xliff:g id="APP_NAME">%s</xliff:g> को आपकी जगह की जानकारी देखने की अनुमति दे रहा है"</string>
@@ -578,4 +592,6 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"जब कोई ऐप्लिकेशन आपके कॉपी किए गए टेक्स्ट, इमेज या अन्य कॉन्टेंट को ऐक्सेस करे, तो मैसेज से इसकी सूचना पाएं"</string>
     <string name="show_password_title" msgid="2877269286984684659">"पासवर्ड दिखाएं"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"टाइप करते समय वर्ण दिखाएं"</string>
+    <!-- no translation found for permission_rationale_message_template (4497650516269082051) -->
+    <skip />
 </resources>
diff --git a/PermissionController/res/values-hr/strings.xml b/PermissionController/res/values-hr/strings.xml
index cfb2b49..0f1d9a3 100644
--- a/PermissionController/res/values-hr/strings.xml
+++ b/PermissionController/res/values-hr/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Zadržite \"Dok se aplikacija koristi\""</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Zadrži \"Samo ovaj put\""</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Više podataka"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Dopusti pristup svim fotografijama"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Odaberi fotografije"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Odaberi više fotografija"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Ne biraj više slika"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ipak nemoj dopustiti"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Odbaci"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> od <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Omogući pristup samo medijima"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Dopusti cijelo vrijeme"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Dopusti samo dok se aplikacija koristi"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Dopusti sve fotografije"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Dopusti odabrane fotografije"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Pitaj svaki put"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Nemoj dopustiti"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Točna lokacija"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Dopustiti apl. &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristup &lt;b&gt;foto/video/audio i drugim datotekama te glazbi&lt;/b&gt; na uređaju?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Želite li dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristup glazbi i audiodatotekama na ovom uređaju?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Želite li dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristup fotografijama i videozapisima na ovom uređaju?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Želite li aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; omogućiti pristup dodatnim fotografijama?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Želite li dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da snima audiozapise?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacija će moći snimati audiozapise samo dok je upotrebljavate"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Želite li dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da snima audiozapise?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Primite poruku kad aplikacije pristupe tekstu, slikama ili drugom kopiranom sadržaju"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Prikaži zaporke"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Nakratko prikaži znakove tijekom unosa"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Aplikacija je navela da može dijeliti <xliff:g id="PERMISSION_NAME">%s</xliff:g> podatke s trećim stranama"</string>
 </resources>
diff --git a/PermissionController/res/values-hu/strings.xml b/PermissionController/res/values-hu/strings.xml
index a525cb2..afd1002 100644
--- a/PermissionController/res/values-hu/strings.xml
+++ b/PermissionController/res/values-hu/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Továbbra is: „Amíg az alkalmazás használatban van”"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Maradjon „Csak most”"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Bővebben"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Hozzáférés engedélyezése az összes fotóhoz"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Fotók kijelölése"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"További fotók kijelölése"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Ne jelöljön ki több fotót"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Semmiképpen se engedélyezze"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Elvetés"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>/<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>."</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Csak a médiatartalmakhoz való hozzáférés engedélyezése"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Mindig engedélyezett"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Csak az alkalmazás használatakor engedélyezett"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Összes fotó engedélyezése"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Kijelölt fotók engedélyezése"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Mindig kérdezzen rá"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Tiltás"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Pontos hely"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Hozzáférhet a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; az eszközön tárolt &lt;b&gt;fotókhoz, hang-, videó- és egyéb fájlokhoz&lt;/b&gt;?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Hozzáférhet a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; az eszközön tárolt zenékhez és egyéb hanganyagokhoz?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Hozzáférhet a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; az eszközön tárolt fotókhoz és videókhoz?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Engedélyezi, hogy a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; további fotókhoz férjen hozzá?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hangfelvételt készíthessen?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Az alkalmazás csak akkor tud majd hangfelvételt készíteni, amikor Ön használja az alkalmazást."</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hangfelvételt készíthessen?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Üzenet megjelenítése, amikor alkalmazások férnek hozzá a vágólapra másolt szövegekhez, képekhez vagy más tartalmakhoz"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Jelszavak mutatása"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Gépelés közben rövid ideig megjeleníti a karaktereket"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Az alkalmazás jelezte, hogy megoszthat a(z) <xliff:g id="PERMISSION_NAME">%s</xliff:g> jogosultsággal kapcsolatos adatokat harmadik felekkel"</string>
 </resources>
diff --git a/PermissionController/res/values-hy/strings.xml b/PermissionController/res/values-hy/strings.xml
index 7854ee6..60af535 100644
--- a/PermissionController/res/values-hy/strings.xml
+++ b/PermissionController/res/values-hy/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Թույլատրել, միայն երբ հավելվածն ակտիվ է"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Պահպանել «Միայն այս անգամ» կարգավորումը"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Մանրամասն"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Հասանելի դարձնել բոլոր լուսանկարները"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Ընտրել լուսանկարներ"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Ընտրել այլ լուսանկարներ"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Չընտրել այլ լուսանկարներ"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Միևնույն է չթույլատրել"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Փակել"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Հասանելի դարձնել միայն մեդիաֆայլերը"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Միշտ թույլատրել"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Թույլատրել միայն հավելվածի օգտագործման ժամանակ"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Թույլատրել բոլոր լուսանկարները"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Թույլատրել ընտրված լուսանկարները"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Ամեն անգամ հարցնել"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Չթույլատրել"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Ճշգրիտ տեղադրություն"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի &lt;b&gt;նկարները, երգերը, տեսանյութերը, աուդիո ֆայլերը և մյուս ֆայլերը&lt;/b&gt;"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի երաժշտությունը և մյուս աուդիո ֆայլերը"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Թույլ տա՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այս սարքի լուսանկարներն ու տեսանյութերը"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Հասանելի դարձնե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին այլ լուսանկարներ։"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին ձայնագրել"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Այս հավելվածը կկարողանա ձայնագրություններ անել միայն, երբ այն օգտագործելիս լինեք"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին ձայնագրություններ անել։"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Ցուցադրել հաղորդագրություն, երբ հավելվածներին հասանելի են դառնում ձեր պատճենած տեքստը, պատկերները կամ այլ բովանդակություն"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Ցուցադրել գաղտնաբառերը"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Տեքստ մուտքագրելիս կարճ ժամանակով ցուցադրել գրանշանները"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Այս հավելվածը կարող է երրորդ կողմերի հետ կիսվել «<xliff:g id="PERMISSION_NAME">%s</xliff:g>» կատեգորիային առնչվող տվյալներով"</string>
 </resources>
diff --git a/PermissionController/res/values-in/strings.xml b/PermissionController/res/values-in/strings.xml
index 0b39e45..ab0929d 100644
--- a/PermissionController/res/values-in/strings.xml
+++ b/PermissionController/res/values-in/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Pertahankan \"Saat aplikasi digunakan\""</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Pertahankan \"Hanya kali ini\""</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Info lengkap"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Izinkan akses ke semua foto"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Pilih foto"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Pilih foto lainnya"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Jangan pilih foto lagi"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Bagaimanapun jangan izinkan"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Tutup"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> dari <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Izinkan akses hanya ke media"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Izinkan sepanjang waktu"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Izinkan hanya saat aplikasi digunakan"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Izinkan semua foto"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Izinkan foto yang dipilih"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Selalu tanya"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Jangan izinkan"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Lokasi akurat"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses &lt;b&gt;foto, video, musik, audio, dan file lainnya&lt;/b&gt; di perangkat ini?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses musik dan audio di perangkat ini?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses foto dan video di perangkat ini?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Beri &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; akses ke foto lainnya?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; merekam audio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikasi hanya dapat merekam audio saat aplikasi sedang digunakan"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; merekam audio?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Menampilkan pesan saat aplikasi mengakses teks, gambar, atau konten lainnya yang telah Anda salin"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Tampilkan sandi"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Menampilkan karakter sejenak saat Anda mengetik"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Aplikasi ini menyatakan bahwa aplikasi mungkin membagikan data <xliff:g id="PERMISSION_NAME">%s</xliff:g> ke pihak ketiga"</string>
 </resources>
diff --git a/PermissionController/res/values-is/strings.xml b/PermissionController/res/values-is/strings.xml
index 97d15ba..1db9c54 100644
--- a/PermissionController/res/values-is/strings.xml
+++ b/PermissionController/res/values-is/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Halda „Þegar forritið er í notkun“"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Halda „Aðeins í þetta skipti“"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Upplýsingar"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Leyfa aðgang að öllum myndum"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Velja myndir"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Velja fleiri myndir"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Ekki velja fleiri myndir"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ekki leyfa þrátt fyrir það"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Loka"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> af <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Leyfa aðeins aðgang að margmiðlunarefni"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Leyfa alltaf"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Leyfa aðeins þegar forritið er í notkun"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Leyfa allar myndir"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Leyfa valdar myndir"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Spyrja alltaf"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Ekki leyfa"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Nákvæm staðsetning"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að &lt;b&gt;myndum, myndskeiðum, tónlist, hljóði og öðrum skrám&lt;/b&gt; í þessu tæki?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að tónlist og hljóði í þessu tæki?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að myndum og myndskeiðum í þessu tæki?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að fleiri myndum?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að taka upp hljóð?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Forritið mun aðeins geta tekið upp hljóð þegar þú ert að nota forritið"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Viltu leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að taka upp hljóð?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Birta skilaboð þegar forrit fá aðgang að texta, myndum eða öðru efni sem þú hefur afritað"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Sýna aðgangsorð"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Birta stafi í stutta stund þegar þú skrifar"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Þetta forrit gaf til kynna að það kunni að deila gögnum af gerðinni „<xliff:g id="PERMISSION_NAME">%s</xliff:g>“ með þriðju aðilum"</string>
 </resources>
diff --git a/PermissionController/res/values-it/strings.xml b/PermissionController/res/values-it/strings.xml
index c37a567..3c61704 100644
--- a/PermissionController/res/values-it/strings.xml
+++ b/PermissionController/res/values-it/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Mantieni \"Mentre l\'app è in uso\""</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Mantieni solo questa volta"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Altre info"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Consenti l\'accesso a tutte le foto"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Seleziona foto"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Seleziona altre foto"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Non selezionare altre foto"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Non consentire comunque"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Ignora"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> di <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Consenti l\'accesso solo ai file multimediali"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Consenti sempre"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Consenti solo mentre l\'app è in uso"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Consenti tutte le foto"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Consenti le foto selezionate"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Chiedi ogni volta"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Non consentire"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Posizione esatta"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere a &lt;b&gt;foto, video, musica, audio e altri file&lt;/b&gt; sul dispositivo?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere a musica e audio sul dispositivo?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere a foto e video sul dispositivo?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Vuoi consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ad altre foto?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di registrare audio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"L\'app potrà registrare audio soltanto quando la usi"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vuoi consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di registrare audio?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Viene mostrato un messaggio quando le app accedono a testo, immagini o altri contenuti che hai copiato"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Mostra password"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Mostra brevemente i caratteri durante la digitazione"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Questa app ha dichiarato che potrebbe condividere dati <xliff:g id="PERMISSION_NAME">%s</xliff:g> con terze parti"</string>
 </resources>
diff --git a/PermissionController/res/values-iw/strings.xml b/PermissionController/res/values-iw/strings.xml
index 696b5ca..dd8a144 100644
--- a/PermissionController/res/values-iw/strings.xml
+++ b/PermissionController/res/values-iw/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"אני רוצה להשאיר את האפשרות \"כשהאפליקציה נמצאת בשימוש\""</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"אני רוצה לשמור על ההגדרה “רק הפעם”"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"מידע נוסף"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"אישור גישה לכל התמונות"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"בחירת תמונות"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"בחירת תמונות נוספות"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"לא לבחור תמונות נוספות"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"אין אישור בכל זאת"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"סגירה"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> מתוך <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"אישור גישה למדיה בלבד"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"כן, כל הזמן"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"רק כשהאפליקציה בשימוש"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"גישה לכל התמונות"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"גישה לתמונות נבחרות"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"יש לשאול בכל פעם"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"אין אישור"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"מיקום מדויק"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"‏לתת לאפליקציה ‎&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‎‏ הרשאת גישה ‎&lt;b&gt;‎‏לתמונות, לסרטונים, למוזיקה, לאודיו ולקבצים אחרים‎&lt;/b&gt;‎‏ במכשיר?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"‏לתת לאפליקציה ‎&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‎‏ הרשאת גישה למוזיקה ולקובצי אודיו במכשיר?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"‏לתת לאפליקציה ‎&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‎‏ הרשאת גישה לתמונות ולסרטונים במכשיר?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; גישה לתמונות נוספות?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"‏לאשר לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; להקליט אודיו?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"האפליקציה תוכל להקליט אודיו רק כאשר היא בשימוש"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאה להקליט אודיו?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"הצגת הודעה בזמן גישה של אפליקציות לטקסט, לתמונות או לכל תוכן אחר שהעתקת"</string>
     <string name="show_password_title" msgid="2877269286984684659">"הצגת סיסמאות"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"התווים יופיעו לפרקי זמן קצרים בזמן ההקלדה"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"האפליקציה הזו הצהירה שהיא עשויה לשתף נתונים של <xliff:g id="PERMISSION_NAME">%s</xliff:g> עם צדדים שלישיים"</string>
 </resources>
diff --git a/PermissionController/res/values-ja/strings.xml b/PermissionController/res/values-ja/strings.xml
index db8fd2c..7d98008 100644
--- a/PermissionController/res/values-ja/strings.xml
+++ b/PermissionController/res/values-ja/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"[アプリが使用中の場合] を保持"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"「今回のみ」の設定を維持"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"詳細"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"すべての写真へのアクセスを許可"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"写真を選択"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"他の写真を選択"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"他の写真を選択しない"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"許可しない"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"閉じる"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"メディアへのアクセスのみを許可"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"常に許可"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"アプリの使用中のみ許可"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"すべての写真を許可"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"一部の写真を許可"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"毎回確認する"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"許可しない"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"正確な現在地"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"このデバイス内の&lt;b&gt;写真、動画、音楽、音声など&lt;/b&gt;へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"このデバイス内の音楽と音声へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"このデバイス内の写真と動画へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に他の写真へのアクセスを許可しますか?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"音声の録音を「&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;」に許可しますか?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"アプリは、ユーザーがアプリを使用している場合のみ音声を録音できます"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"音声の録音を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"クリップボードにコピーしたテキストや画像などにアプリがアクセスすると、メッセージで通知する"</string>
     <string name="show_password_title" msgid="2877269286984684659">"パスワードの表示"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"入力した文字を短い間表示する"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"このアプリは、<xliff:g id="PERMISSION_NAME">%s</xliff:g>データをサードパーティと共有する可能性があります"</string>
 </resources>
diff --git a/PermissionController/res/values-ka/strings.xml b/PermissionController/res/values-ka/strings.xml
index f4ed285..d3959c0 100644
--- a/PermissionController/res/values-ka/strings.xml
+++ b/PermissionController/res/values-ka/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"დარჩეს „აპის გამოყენებისას“"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"დაშვება „მხოლოდ ამ ერთხელ“"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"მეტი ინფორმაცია"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"ყველა ფოტოზე წვდომის დაშვება"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ფოტოების არჩევა"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"მეტი ფოტოს არჩევა"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"არ აირჩიო მეტი ფოტო"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"მაინც არ დაიშვას"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"დახურვა"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>-დან"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"მხოლოდ მედიაზე წვდომის დაშვება"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ყოველთვის დაშვება"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"მხოლოდ აპის გამოყენებისას დაშვება"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"ყველა ფოტოს დაშვება"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"არჩეული ფოტოების დაშვება"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"ყოველთვის მკითხეთ"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"არ დაიშვას"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"ზუსტი მდებარეობა"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"მიანიჭებთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-ის წვდომას თქვენი მოწყ. &lt;b&gt;ფოტოებზე, ვიდეოებზე, მუსიკაზე, აუდიო და სხვა &lt;/b&gt;?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"მიანიჭებთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-ს წვდომას თქვენი მოწყობილობის მუსიკასა და აუდიოფაილებზე?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"მიანიჭებთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-ის წვდომას თქვენი მოწყობილობის ფოტოებსა და ვიდეოებზე?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"მეტ ფოტოზე წვდომის ნებას დართავთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"გსურთ, მიანიჭოთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-ს&lt;/b&gt; აუდიოს ჩაწერის ნებართვა?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ეს აპი აუდიოს ჩაწერას მხოლოდ მაშინ შეძლებს, როცა მას იყენებთ"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"გსურთ, მიანიჭოთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-ს&lt;/b&gt; აუდიოს ჩაწერის ნებართვა?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"როდესაც აპებს თქვენ მიერ კოპირებულ ტექსტზე, სურათებზე ან სხვა კონტენტზე აქვთ წვდომა, გამოჩნდება შეტყობინება"</string>
     <string name="show_password_title" msgid="2877269286984684659">"პაროლების ჩვენება"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"აკრეფისას სიმბოლოების ხანმოკლედ გამოჩენა"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"ამ აპის თანახმად, მან შესაძლოა გაუზიაროს <xliff:g id="PERMISSION_NAME">%s</xliff:g>-ის მონაცემები მესამე მხარეს"</string>
 </resources>
diff --git a/PermissionController/res/values-kk/strings.xml b/PermissionController/res/values-kk/strings.xml
index 1b98346..65ab340 100644
--- a/PermissionController/res/values-kk/strings.xml
+++ b/PermissionController/res/values-kk/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"\"Қолданба пайдаланылатын кезде\" түймесін басып тұрыңыз"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\"Тек осы жолы\" күйінде қалдыру"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Толығырақ"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Барлық фотосуретті пайдалануға рұқсат беру"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Фотосурет таңдау"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Көбірек фотосурет таңдау"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Басқа фотосурет таңдамау"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Бәрібір рұқсат бермеу"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Жабу"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Тек медиафайлдарды пайдалануға рұқсат беру"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Біржола рұқсат беру"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Қолданбаны пайдаланғанда ғана рұқсат беру"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Барлық фотосуретке рұқсат беру"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Таңдалған фотосуреттерге рұқсат беру"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Әрдайым сұрау"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Рұқсат бермеу"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Нақты орын"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына осы құрылғыдағы &lt;b&gt;фотосурет, бейне, музыка, аудио мен басқа файлдарды&lt;/b&gt; пайдалану рұқсаты берілсін бе?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына осы құрылғыдағы музыка мен аудионы пайдалану рұқсаты берілсін бе?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына осы құрылғыдағы фотосурет пен бейнені пайдалану рұқсаты берілсін бе?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына көбірек фотосурет пайдалануға рұқсат беру керек пе?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына дыбыс жазуға рұқсат берілсін бе?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Қолданба тек жұмыс кезінде ғана аудиомазмұн жаза алады."</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына аудиомазмұн жазуға рұқсат берілсін бе?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Қолданбалар көшірілген мәтінді, суреттерді немесе басқа мазмұнды пайдаланған кезде хабар көрсету"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Құпия сөздерді көрсету"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Таңбалар терілген кезде аз уақыт көрсетіледі."</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Бұл қолданба <xliff:g id="PERMISSION_NAME">%s</xliff:g> деректерін үшінші тараптармен бөлісуі мүмкін екенін мәлімдеді."</string>
 </resources>
diff --git a/PermissionController/res/values-km/strings.xml b/PermissionController/res/values-km/strings.xml
index 698b891..45aad0f 100644
--- a/PermissionController/res/values-km/strings.xml
+++ b/PermissionController/res/values-km/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"រក្សាទុក “ខណៈពេល​កំពុងប្រើ​កម្មវិធី”"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"រក្សាទុក “តែពេលនេះ​ប៉ុណ្ណោះ”"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"ព័ត៌មាន​បន្ថែម"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"អនុញ្ញាតឱ្យចូលប្រើរូបថតទាំងអស់"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ជ្រើសរើសរូបថត"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"ជ្រើសរើស​រូបថតច្រើន​ទៀត"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"កុំជ្រើសរើសរូបថតបន្ថែមទៀត"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"មិនអីទេ មិនអនុញ្ញាត"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ច្រានចោល"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> ក្នុងចំណោម <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"អនុញ្ញាតឱ្យចូលប្រើ​មេឌៀតែប៉ុណ្ណោះ"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"អនុញ្ញាតគ្រប់ពេល"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"អនុញ្ញាត​ពេល​កំពុង​ប្រើប្រាស់​កម្មវិធីតែ​ប៉ុណ្ណោះ"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"អនុញ្ញាតរូបថតទាំងអស់"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"អនុញ្ញាតរូបថតដែលបានជ្រើសរើស"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"សួរគ្រប់ពេល"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"មិនអនុញ្ញាត"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"ទីតាំងជាក់លាក់"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើ &lt;b&gt;រូបថត វីដេអូ តន្ត្រី សំឡេង និងឯកសារផ្សេងទៀត&lt;/b&gt;នៅលើឧបករណ៍នេះទេ?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើប្រាស់តន្ត្រី និងសំឡេងនៅលើឧបករណ៍នេះទេ?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើប្រាស់រូបថត និងវីដេអូនៅលើឧបករណ៍នេះទេ?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"ផ្ដល់សិទ្ធិឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើរូបថតបន្ថែមទៀតឬ?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ថតសំឡេង?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"កម្មវិធីនេះនឹងអាចថតសំឡេង នៅពេលអ្នកកំពុងប្រើប្រាស់កម្មវិធីតែប៉ុណ្ណោះ"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ថតសំឡេងឬ?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"បង្ហាញសារ នៅពេលកម្មវិធីចូលប្រើអត្ថបទ រូបភាព ឬខ្លឹមសារផ្សេងទៀតដែលអ្នកបានចម្លង"</string>
     <string name="show_password_title" msgid="2877269286984684659">"បង្ហាញ​ពាក្យ​សម្ងាត់"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"បង្ហាញ​តួអក្សរ​មួយភ្លែត​ខណៈ​ពេល​អ្នក​វាយ​បញ្ចូល"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"កម្មវិធីនេះបានបញ្ជាក់ថា វាអាចចែករំលែកទិន្នន័យ <xliff:g id="PERMISSION_NAME">%s</xliff:g> ជាមួយភាគីទីបី"</string>
 </resources>
diff --git a/PermissionController/res/values-kn/strings.xml b/PermissionController/res/values-kn/strings.xml
index ef2fd79..a6267ed 100644
--- a/PermissionController/res/values-kn/strings.xml
+++ b/PermissionController/res/values-kn/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“ಆ್ಯಪ್ ಬಳಕೆಯಲ್ಲಿರುವಾಗ” ಹಾಗೆಯೇ ಇರಿಸಿ"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“ಈ ಬಾರಿ ಮಾತ್ರ” ಇರಿಸಿ"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"ಹೆಚ್ಚಿನ ಮಾಹಿತಿ"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"ಎಲ್ಲಾ ಫೋಟೋಗಳಿಗೆ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಅನುಮತಿಸಿ"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ಫೋಟೋಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"ಇನ್ನಷ್ಟು ಫೋಟೋಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"ಇನ್ನಷ್ಟು ಫೋಟೋಗಳನ್ನು ಆಯ್ಕೆ ಮಾಡಬೇಡಿ"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"ಯಾವುದೇ ರೀತಿಯಲ್ಲೂ ಅನುಮತಿಸಬೇಡಿ"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ವಜಾಗೊಳಿಸಿ"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> ರಲ್ಲಿ <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"ಮಾಧ್ಯಮಕ್ಕೆ ಮಾತ್ರ ಪ್ರವೇಶಿಸಲು ಅನುಮತಿಸಿ"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ಎಲ್ಲಾ ಸಮಯದಲ್ಲೂ ಅನುಮತಿಸಿ"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"ಆ್ಯಪ್ ಬಳಸುವಾಗ ಮಾತ್ರ ಅನುಮತಿಸಿ"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"ಎಲ್ಲಾ ಫೋಟೋಗಳನ್ನು ಅನುಮತಿಸಿ"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"ಆಯ್ಕೆಮಾಡಿದ ಫೋಟೋಗಳನ್ನು ಅನುಮತಿಸಿ"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"ಪ್ರತಿ ಬಾರಿ ಕೇಳಿ"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"ಅನುಮತಿಸಬೇಡಿ"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"ನಿಖರವಾದ ಸ್ಥಾನ"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"ಈ ಸಾಧನದಲ್ಲಿರುವ &lt;b&gt;ಫೋಟೋಗಳು, ವೀಡಿಯೊಗಳು, ಸಂಗೀತ, ಆಡಿಯೋ, ಇತರ ಫೈಲ್‌ಗಳನ್ನು&lt;/b&gt; ಪ್ರವೇಶಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"ಈ ಸಾಧನದಲ್ಲಿರುವ ಸಂಗೀತ ಮತ್ತು ಆಡಿಯೊವನ್ನು ಪ್ರವೇಶಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"ಈ ಸಾಧನದಲ್ಲಿರುವ ಫೋಟೋಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳನ್ನು ಪ್ರವೇಶಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"ಇನ್ನಷ್ಟು ಫೋಟೋಗಳಿಗೆ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಆ್ಯಕ್ಸೆಸ್ ನೀಡಬೇಕೇ?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"ಆಡಿಯೋ ರೆಕಾರ್ಡ್‌ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ನೀವು ಆ್ಯಪ್ ಬಳಸುತ್ತಿರುವಾಗ ಮಾತ್ರ ಆ್ಯಪ್‌ಗೆ ಆಡಿಯೋ ರೆಕಾರ್ಡ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗುತ್ತದೆ"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"ಆಡಿಯೋ ರೆಕಾರ್ಡ್‌ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"ನೀವು ನಕಲಿಸಿರುವ ಪಠ್ಯ, ಚಿತ್ರಗಳು ಅಥವಾ ಇತರ ವಿಷಯವನ್ನು ಆ್ಯಪ್‌ಗಳು ಪ್ರವೇಶಿಸಿದಾಗ ಸಂದೇಶವೊಂದನ್ನು ತೋರಿಸಿ"</string>
     <string name="show_password_title" msgid="2877269286984684659">"ಪಾಸ್‌ವರ್ಡ್‌ಗಳನ್ನು ತೋರಿಸಿ"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"ನೀವು ಟೈಪ್ ಮಾಡಿದಂತೆ ಅಕ್ಷರಗಳನ್ನು ಸಂಕ್ಷಿಪ್ತವಾಗಿ ಪ್ರದರ್ಶಿಸಿ"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"ಈ ಆ್ಯಪ್ ಥರ್ಡ್ ಪಾರ್ಟಿಗಳೊಂದಿಗೆ <xliff:g id="PERMISSION_NAME">%s</xliff:g> ಡೇಟಾವನ್ನು ಹಂಚಿಕೊಳ್ಳಬಹುದು ಎಂದು ಉಲ್ಲೇಖಿಸಿದೆ"</string>
 </resources>
diff --git a/PermissionController/res/values-ko/strings.xml b/PermissionController/res/values-ko/strings.xml
index d2eba81..81e0e41 100644
--- a/PermissionController/res/values-ko/strings.xml
+++ b/PermissionController/res/values-ko/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"\'앱 사용 중에만 허용\' 유지"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\'이번만 허용\' 유지"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"추가 정보"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"모든 사진에 대한 액세스 허용"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"사진 선택"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"추가 사진 선택"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"추가 사진 선택 안함"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"무시하고 허용 안함"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"닫기"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"미디어 액세스만 허용"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"항상 허용"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"앱 사용 중에만 허용"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"모든 사진 허용"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"선택한 사진 허용"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"항상 확인"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"허용 안함"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"정확한 위치"</string>
@@ -449,9 +455,9 @@
     <string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"음성 어시스턴트 활성화를 위해 마이크가 사용되면 상태 표시줄에 아이콘 표시"</string>
     <string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 기기의 사진 및 미디어에 액세스하도록 허용하시겠습니까?"</string>
     <string name="permgrouprequest_contacts" msgid="8391550064551053695">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 내 연락처에 액세스하도록 허용하시겠습니까?"</string>
-    <string name="permgrouprequest_location" msgid="6990232580121067883">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 내 기기 위치에 액세스하도록 허용하시겠습니까?"</string>
+    <string name="permgrouprequest_location" msgid="6990232580121067883">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 내 기기의 위치 정보에 액세스하도록 허용하시겠습니까?"</string>
     <string name="permgrouprequestdetail_location" msgid="2635935335778429894">"앱을 사용할 때만 앱에서 위치에 액세스합니다."</string>
-    <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 내 기기 위치에 액세스하도록 허용하시겠습니까?"</string>
+    <string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 내 기기의 위치 정보에 액세스하도록 허용하시겠습니까?"</string>
     <string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"앱을 사용하고 있지 않을 때도 앱에서 내 위치에 항상 액세스하려고 할 수 있습니다. "<annotation id="link">"설정에서 액세스를 허용"</annotation>"하세요."</string>
     <string name="permgroupupgraderequest_location" msgid="8328408946822691636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;의 위치 액세스 권한을 변경하시겠습니까?"</string>
     <string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"앱을 사용하고 있지 않을 때도 앱에서 내 위치에 항상 액세스하려고 합니다. "<annotation id="link">"설정에서 액세스를 허용"</annotation>"하세요."</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 기기의 &lt;b&gt;사진, 동영상, 음악, 오디오, 기타 파일&lt;/b&gt;에 액세스하도록 허용하시겠습니까?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 기기의 음악과 오디오에 액세스하도록 허용하시겠습니까?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 기기의 사진과 동영상에 액세스하도록 허용하시겠습니까?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에 추가 사진에 대한 액세스를 허용하시겠습니까?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 오디오를 녹음하도록 허용하시겠습니까?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"앱을 사용하고 있는 동안에만 앱에서 오디오를 녹음할 수 있습니다."</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 오디오를 녹음하도록 허용하시겠습니까?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"앱이 복사된 텍스트, 이미지 또는 기타 콘텐츠에 액세스할 때 메시지 표시"</string>
     <string name="show_password_title" msgid="2877269286984684659">"비밀번호 표시"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"입력할 때 잠깐 표시"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"앱에서 <xliff:g id="PERMISSION_NAME">%s</xliff:g> 데이터를 서드 파티와 공유할 수 있다고 명시했습니다."</string>
 </resources>
diff --git a/PermissionController/res/values-ky/strings.xml b/PermissionController/res/values-ky/strings.xml
index 700854a..2498316 100644
--- a/PermissionController/res/values-ky/strings.xml
+++ b/PermissionController/res/values-ky/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"\"Колдонмо колдонулуп жатканда\" режими кала берсин"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\"Ушул жолу гана\" жөндөөсү калсын"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Дагы маалымат"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Бардык сүрөттөргө кирүүгө уруксат берүү"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Cүрөттөрдү тандаңыз"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Дагы сүрөт тандоо"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Дагы сүрөттөр тандалбасын"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Баары бир тыюу салуу"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Жабуу"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> уруксаттын ичинен <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Медиа файлдарга гана уруксат берүү"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Бардык учурда уруксат берилет"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Колдонмо колдонулуп жатканда гана уруксат берилет"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Бардык сүрөттөргө уруксат берүү"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Тандалган сүрөттөргө уруксат берүү"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Ар дайым суралсын"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Жок"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Так жайгашкан жери"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна ушул түзмөктөгү &lt;b&gt;сүрөттөрдү, видеолорду, ырларды, аудио файлдарды жана башка нерселерди&lt;/b&gt; жеткиликтүү кыласызбы?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна ушул түзмөктөгү ырлар менен аудио файлдарды жеткиликтүү кыласызбы?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна ушул түзмөктөгү сүрөттөр менен видеолорду жеткиликтүү кыласызбы?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна көбүрөөк сүрөттөргө кирүүгө уруксат берилсинби?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна аудио файлдарды жаздырганга уруксат бересизби?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Колдонмону колдонуп жатканда гана, ал аудио жаздыра алат"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна аудио файлдарды жаздырууга уруксат бересизби?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Колдонмолор көчүрүлгөн текстти, сүрөттөрдү же башка нерселерди пайдаланганда билдирүүлөр көрүнөт"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Сырсөз көрүнсүн"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Сырсөз терилип жатканда символдор бир саамга көрүнөт"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Бул колдонмо <xliff:g id="PERMISSION_NAME">%s</xliff:g> тууралуу маалыматты үчүнчү тараптар менен бөлүшүүгө уруксат сурап жатат"</string>
 </resources>
diff --git a/PermissionController/res/values-lo/strings.xml b/PermissionController/res/values-lo/strings.xml
index d6b3303..c2de2b9 100644
--- a/PermissionController/res/values-lo/strings.xml
+++ b/PermissionController/res/values-lo/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"ໃຊ້ “ໃນຂະນະທີ່ມີການໃຊ້ແອັບຢູ່”"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"ໃຊ້ແບບ “ສະເພາະເທື່ອນີ້” ຕໍ່ໄປ"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"ຂໍ້ມູນເພີ່ມເຕີມ"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"ອະນຸຍາດສິດເຂົ້າເຖິງຮູບພາບທັງໝົດ"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ເລືອກຮູບພາບ"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"ເລືອກຮູບພາບເພີ່ມເຕີມ"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"ຢ່າເລືອກຮູບພາບເພີ່ມເຕີມ"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"ຢືນຢັນບໍ່ອະນຸຍາດ"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ປິດໄວ້"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> ຈາກທັງໝົດ <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"ອະນຸຍາດການເຂົ້າເຖິງມີເດຍເທົ່ານັ້ນ"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ອະນຸຍາດຕະຫຼອດເວລາ"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"ອະນຸຍາດໃນເວລາທີ່ກຳລັງໃຊ້ແອັບເທົ່ານັ້ນ"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"ອະນຸຍາດຮູບພາບທັງໝົດ"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"ອະນຸຍາດຮູບພາບທີ່ເລືອກໄວ້"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"ຖາມທຸກເທື່ອ"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"ບໍ່ອະນຸຍາດ"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"ສະຖານທີ່ແບບລະອຽດ"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງ &lt;b&gt;ຮູບພາບ, ວິດີໂອ, ເພງ, ສຽງ ແລະ ໄຟລ໌ອື່ນໆ&lt;/b&gt; ຢູ່ອຸປະກອນນີ້ບໍ?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງເພງ ແລະ ສຽງຢູ່ອຸປະກອນນີ້ບໍ?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງຮູບພາບ ແລະ ວິດີໂອຢູ່ອຸປະກອນນີ້ບໍ?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"ໃຫ້ສິດ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ສິດເຂົ້າເຖິງຮູບພາບເພີ່ມເຕີມບໍ?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"ອະນຸຍາດ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ໃຫ້ບັນທຶກສຽງບໍ?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ແອັບນີ້ສາມາດບັນທຶກສຽງໃນຂະນະທີ່ທ່ານກຳລັງໃຊ້ແອັບເທົ່ານັ້ນ"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"ອະນຸຍາດ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ໃຫ້ບັນທຶກສຽງບໍ?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"ສະແດງຂໍ້ຄວາມເມື່ອແອັບເຂົ້າເຖິງຂໍ້ຄວາມ, ຮູບພາບ ຫຼື ເນື້ອຫາອື່ນທີ່ທ່ານສຳເນົາໄວ້"</string>
     <string name="show_password_title" msgid="2877269286984684659">"ສະແດງລະຫັດຜ່ານ"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"ສະແດງຕົວອັກສອນເປັນເວລາສັ້ນໆໃນເວລາພິມ"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"ແອັບນີ້ລະບຸວ່າແອັບອາດແບ່ງປັນຂໍ້ມູນ <xliff:g id="PERMISSION_NAME">%s</xliff:g> ກັບພາກສ່ວນທີສາມ"</string>
 </resources>
diff --git a/PermissionController/res/values-lt/strings.xml b/PermissionController/res/values-lt/strings.xml
index e530bb6..6d13307 100644
--- a/PermissionController/res/values-lt/strings.xml
+++ b/PermissionController/res/values-lt/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Išlaikyti režimą „Kai programa naudojama“ įjungtą"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Palikti „Tik šį kartą“"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Daugiau inform."</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Leisti pasiekti visas nuotraukas"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Pasirinkti nuotraukas"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Pasirinkti daugiau nuotraukų"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Nesirinkti daugiau nuotraukų"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Vis tiek neleisti"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Atmesti"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> iš <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Leisti pasiekti tik mediją"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Leisti visą laiką"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Leisti tik naudojant programą"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Leisti visas nuotraukas"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Leisti pasirinktas nuotraukas"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Klausti kaskart"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Neleisti"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Tiksli vietovė"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti &lt;b&gt;nuotrauk., vaizdo, garso įrašus, muziką, kitus failus&lt;/b&gt; įrenginyje?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti muziką ir garso failus šiame įrenginyje?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti nuotraukas ir vaizdo įrašus šiame įrenginyje?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti daugiau nuotraukų?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; įrašyti garsą?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Programa galės įrašyti garsą, tik kai ją naudosite"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; įrašyti garsą?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Rodyti pranešimą, kai programos pasiekia nukopijuotą tekstą, vaizdus ar kitą turinį"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Rodyti slaptažodžius"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Trumpai rodyti simbolius vedant tekstą"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Ši programa nurodė, kad gali bendrinti <xliff:g id="PERMISSION_NAME">%s</xliff:g> duomenis su trečiosiomis šalimis"</string>
 </resources>
diff --git a/PermissionController/res/values-lv/strings.xml b/PermissionController/res/values-lv/strings.xml
index 8d95973..f51bbeb 100644
--- a/PermissionController/res/values-lv/strings.xml
+++ b/PermissionController/res/values-lv/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Paturēt atļauju “Kamēr lietotne tiek izmantota”"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Paturēt iestatījumu “Tikai šoreiz”"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Informācija"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Atļaut piekļuvi visiem fotoattēliem"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Atlasīt fotoattēlus"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Atlasīt vairāk fotoattēlu"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Neatlasīt citus fotoattēlus"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Tomēr neatļaut"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Nerādīt"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>. no <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Atļaut piekļūt tikai multivides failiem"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Vienmēr atļaut"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Atļaut tikai lietotnes izmantošanas laikā"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Atļaut visus fotoattēlus"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Atļaut atlasītos fotoattēlus"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Vaicāt katru reizi"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Neatļaut"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Precīza atrašanās vieta"</string>
@@ -425,7 +431,7 @@
     <string name="car_default_app_selected" msgid="5416420830430644174">"Atlasīta"</string>
     <string name="car_default_app_selected_with_info" msgid="1932204186080593500">"Atlasīta — <xliff:g id="ADDITIONAL_INFO">%1$s</xliff:g>"</string>
     <string name="special_app_access_search_keyword" msgid="8032347212290774210">"Īpaša lietotņu piekļuve"</string>
-    <string name="special_app_access" msgid="5019319067120213797">"Īpaša piekļuve lietotnēm"</string>
+    <string name="special_app_access" msgid="5019319067120213797">"Īpaša lietotņu piekļuve"</string>
     <string name="no_special_app_access" msgid="6950277571805106247">"Nav īpašas piekļuves lietotnēm"</string>
     <string name="special_app_access_no_apps" msgid="4102911722787886970">"Nav lietotņu"</string>
     <string name="home_missing_work_profile_support" msgid="1756855847669387977">"Darba profils netiek atbalstīts"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt &lt;b&gt;foto, video, mūzikai, audio u.c. failiem&lt;/b&gt; ierīcē?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt mūzikai un audio failiem šajā ierīcē?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt fotoattēliem un video šajā ierīcē?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Vai piešķirt lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļuvi citiem fotoattēliem?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ierakstīt audio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Lietotne varēs ierakstīt audio tikai tad, kad izmantosiet lietotni."</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ierakstīt audio?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Rādīt ziņojumu, kad lietotnes piekļūst jūsu nokopētajam tekstam, attēliem vai citam saturam"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Rādīt paroles"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Rakstot tiek īslaicīgi rādītas rakstzīmes"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Lietotne norādīja, ka tā var kopīgot ar trešajām pusēm šādus datus: <xliff:g id="PERMISSION_NAME">%s</xliff:g>"</string>
 </resources>
diff --git a/PermissionController/res/values-mk/strings.xml b/PermissionController/res/values-mk/strings.xml
index 0bf9386..f33e83e 100644
--- a/PermissionController/res/values-mk/strings.xml
+++ b/PermissionController/res/values-mk/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Задржи ја „Додека се користи апликацијата“"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Задржи „Само овој пат“"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Уште информации"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Дозволете пристап до сите фотографии"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Изберете фотографии"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Изберете повеќе фотографии"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Не избирајте повеќе фотографии"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Сепак не дозволувај"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Отфрли"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> од <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Дозволи пристап само до аудиовизуелните содржини"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Дозволи цело време"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Дозволи само додека се користи апликацијата"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Дозволете ги сите фотографии"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Дозволете избрани фотографии"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Прашувај секогаш"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Не дозволувај"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Прецизна локација"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до &lt;b&gt;фотографии, видеа, музика, аудио и други датотеки&lt;/b&gt; на уредов?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до музика и аудиодатотеки на уредов?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до фотографии и видеа на уредов?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Да се дозволи пристап на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; до повеќе фотографии?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да снима аудио?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Апликацијава ќе може да снима аудио само додека ја користите"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да снима аудио?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Нека се прикажува известување кога апликациите пристапуваат до текст, слики или други содржини што сте ги копирале"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Прикажувај ги лозинките"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Прикажувај ги знаците накратко додека пишувам"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Апликацијава изјави дека можеби ќе споделува податоци за <xliff:g id="PERMISSION_NAME">%s</xliff:g> со трети страни"</string>
 </resources>
diff --git a/PermissionController/res/values-ml/strings.xml b/PermissionController/res/values-ml/strings.xml
index ff698b6..fd554f9 100644
--- a/PermissionController/res/values-ml/strings.xml
+++ b/PermissionController/res/values-ml/strings.xml
@@ -32,6 +32,14 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“ആപ്പ് ഉപയോഗത്തിലിരിക്കുമ്പോൾ” തുടരുക"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“ഇപ്പോഴത്തേക്ക് മാത്രം” നിലനിർത്തുക"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"കൂടുതൽ വിവരങ്ങൾ"</string>
+    <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) -->
+    <skip />
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"എന്തായാലും അനുവദിക്കരുത്"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ഡിസ്‌മിസ് ചെയ്യുക"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>-ൽ <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> എണ്ണം"</string>
@@ -186,6 +194,10 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"മീഡിയ ഫയലുകളിലേക്ക് മാത്രം ആക്‌സസ് അനുവദിക്കുക"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ഏതുസമയത്തും അനുവദിക്കുക"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"ആപ്പ് ഉപയോഗിക്കുമ്പോൾ മാത്രം"</string>
+    <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) -->
+    <skip />
+    <!-- no translation found for app_permission_button_select_photos (1022930616634145364) -->
+    <skip />
     <string name="app_permission_button_ask" msgid="3342950658789427">"എപ്പോഴും ചോദിക്കുക"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"അനുവദിക്കരുത്"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"കൃത്യമായ ലൊക്കേഷൻ"</string>
@@ -468,6 +480,8 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;ഫോട്ടോ, വീഡിയോ, സംഗീതം, ഓഡിയോ, മറ്റ് ഫയലുകൾ&lt;/b&gt; എന്നിവയിലേക്ക് &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിന് ആക്സസ് നൽകണോ?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"ഈ ഉപകരണത്തിലെ സംഗീതവും ഓഡിയോയും ആക്സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"ഈ ഉപകരണത്തിലെ ഫോട്ടോകളും വീഡിയോകളും ആക്‌സസ് ചെയ്യാൻ &lt;b&gt; <xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
+    <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) -->
+    <skip />
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"ഓഡിയോ റെക്കോർഡ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ആപ്പിനെ അനുവദിക്കണോ?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"നിങ്ങൾ ആപ്പ് ഉപയോഗിക്കുമ്പോൾ മാത്രമേ അതിന് ഓഡിയോ റെക്കോർഡ് ചെയ്യാൻ കഴിയൂ"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"ഓഡിയോ റെക്കോർഡ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ആപ്പിനെ അനുവദിക്കണോ?"</string>
@@ -578,4 +592,6 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"നിങ്ങൾ പകർത്തിയ ടെക്‌സ്‌റ്റോ ചിത്രങ്ങളോ മറ്റ് ഉള്ളടക്കമോ ആപ്പുകൾ ആക്‌സസ് ചെയ്യുമ്പോൾ ഒരു സന്ദേശം കാണിക്കുക"</string>
     <string name="show_password_title" msgid="2877269286984684659">"പാസ്‌വേ‌ഡുകൾ കാണിക്കുക"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"ടൈപ്പ് ചെയ്യുന്ന അക്ഷരങ്ങൾ പ്രദർശിപ്പിക്കുക"</string>
+    <!-- no translation found for permission_rationale_message_template (4497650516269082051) -->
+    <skip />
 </resources>
diff --git a/PermissionController/res/values-mn/strings.xml b/PermissionController/res/values-mn/strings.xml
index d09b7cd..5f9d171 100644
--- a/PermissionController/res/values-mn/strings.xml
+++ b/PermissionController/res/values-mn/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"\"Аппыг ашиглаж байх үед\" хэвээр үлдээх"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“Зөвхөн энэ удаад зөвшөөрөх”-г хэвээр хадгалах"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Дэлгэрэнгүй мэдээлэл"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Бүх зурагт хандахыг зөвшөөрөх"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Зураг сонгох"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Илүү олон зураг сонгох"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Илүү олон зураг сонгохгүй"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ямартай ч бүү зөвшөөр"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Үл хэрэгсэх"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>-н <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Зөвхөн медиад хандахыг зөвшөөрөх"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Ямар ч үед зөвшөөрөх"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Зөвхөн аппыг ашиглаж байх үед зөвшөөрөх"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Бүх зургийг зөвшөөрөх"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Сонгосон зургуудыг зөвшөөрөх"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Тухай бүрд асуух"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Бүү зөвшөөр"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Нарийвчилсан байршил"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д энэ төхөөрөмжийн &lt;b&gt;зураг, видео, хөгжим, аудио, бусад файлд&lt;/b&gt; хандахыг зөвшөөрөх үү?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д энэ төхөөрөмж дээрх хөгжим болон аудионд хандахыг зөвшөөрөх үү?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д энэ төхөөрөмж дээрх зураг болон видеонд хандахыг зөвшөөрөх үү?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д илүү олон зурагт хандах эрх өгөх үү?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д аудио бичихийг зөвшөөрөх үү?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Энэ апп зөвхөн таныг ашиглаж байх үед л аудио бичих боломжтой болно"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д аудио бичихийг зөвшөөрөх үү?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Аппууд таны хуулсан текст, зураг эсвэл бусад контентод хандах үед мессеж харуулах"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Нууц үгнүүдийг харуулах"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Таныг бичиж явцад тэмдэгтүүдийг түр үзүүлэх"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Энэ апп <xliff:g id="PERMISSION_NAME">%s</xliff:g>-н өгөгдлийг гуравдагч талуудтай хуваалцаж болохыг мэдэгдсэн"</string>
 </resources>
diff --git a/PermissionController/res/values-mr/strings.xml b/PermissionController/res/values-mr/strings.xml
index 69c50c9..5f51f97 100644
--- a/PermissionController/res/values-mr/strings.xml
+++ b/PermissionController/res/values-mr/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“ॲप वापरत असताना” ठेवा"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“फक्त यावेळेपुरते” ठेवा"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"अधिक माहिती"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"सर्व फोटो ॲक्सेस करण्याची अनुमती द्या"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"फोटो निवडा"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"आणखी फोटो निवडा"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"आणखी फोटो निवडू नका"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"तरीही अनुमती देऊ नका"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"डिसमिस करा"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> पैकी <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"फक्त मीडिया ॲक्सेस करण्यासाठी अनुमती द्या"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"सर्व वेळी अनुमती द्या"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"फक्त अ‍ॅप वापरत असताना अनुमती द्या"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"सर्व फोटोना अनुमती द्या"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"निवडलेल्या फोटोना अनुमती द्या"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"प्रत्येक वेळी विचारा"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"अनुमती देऊ नका"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"अचूक स्थान"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला या डिव्हाइसवरील &lt;b&gt;फोटो, व्हिडिओ, संगीत, ऑडिओ व इतर फाइल&lt;/b&gt; अ‍ॅक्सेस करू द्यायच्या?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला या डिव्हाइसवरील संगीत आणि ऑडिओ अ‍ॅक्सेस करण्याची अनुमती द्यायची आहे का?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला या डिव्हाइसवरील फोटो आणि व्हिडिओ अ‍ॅक्सेस करण्याची अनुमती द्यायची आहे का?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला आणखी फोटो अ‍ॅक्सेस करण्याची अनुमती द्यायची आहे का?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला ऑडिओ रेकॉर्ड करू द्यायचा?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ॲप फक्त तुम्ही ॲप वापरत असतानाच ऑडिओ रेकॉर्ड करू शकते"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला ऑडिओ रेकॉर्ड करायची अनुमती द्यायची?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"तुम्ही कॉपी केलेला मजकूर, इमेज किंवा इतर आशय ॲप्स अ‍ॅक्सेस करतात तेव्हा, मेसेज दाखवा"</string>
     <string name="show_password_title" msgid="2877269286984684659">"पासवर्ड दाखवा"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"तुम्ही टाइप कराल त्‍याप्रमाणे वर्ण थोडक्‍यात डिस्प्ले करा"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"हे अ‍ॅप तृतीय पक्षांसोबत <xliff:g id="PERMISSION_NAME">%s</xliff:g> डेटा शेअर करू शकते असे या अ‍ॅपने नमूद केले आहे"</string>
 </resources>
diff --git a/PermissionController/res/values-ms/strings.xml b/PermissionController/res/values-ms/strings.xml
index 2b875e5..675a595 100644
--- a/PermissionController/res/values-ms/strings.xml
+++ b/PermissionController/res/values-ms/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Kekalkan “Semasa apl sedang digunakan”"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Simpan “Kali ini sahaja”"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Lagi maklumat"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Benarkan akses kepada semua foto"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Pilih foto"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Pilih lagi foto"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Jangan pilih lebih banyak foto"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Jangan benarkan juga"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Tolak"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> daripada <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Benarkan akses kepada media sahaja"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Benarkan sepanjang masa"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Benarkan hanya semasa menggunakan apl"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Benarkan semua foto"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Benarkan foto dipilih"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Tanya setiap kali"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Jangan benarkan"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Lokasi tepat"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses &lt;b&gt;foto, video, muzik, audio dan fail lain&lt;/b&gt; pada peranti ini?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses muzik dan audio pada peranti ini?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses foto dan video pada peranti ini?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Beri &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; akses kepada lebih banyak foto?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; merakam audio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Apl hanya boleh merakam audio semasa anda menggunakan apl tersebut"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; merakam audio?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Tunjukkan mesej apabila apl mengakses teks, imej atau kandungan lain yang telah anda salin"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Tunjukkan kata laluan"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Paparkan aksara seketika sambil anda menaip"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Apl ini menyatakan bahawa data <xliff:g id="PERMISSION_NAME">%s</xliff:g> mungkin dikongsikan dengan pihak ketiga"</string>
 </resources>
diff --git a/PermissionController/res/values-my/strings.xml b/PermissionController/res/values-my/strings.xml
index ae86605..61d5b62 100644
--- a/PermissionController/res/values-my/strings.xml
+++ b/PermissionController/res/values-my/strings.xml
@@ -32,6 +32,14 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"\"အက်ပ်ကို အသုံးပြုနေစဉ်\" ကို ဆက်ထားရန်"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\"ဤတစ်ကြိမ်သာ\" ကို မှတ်ထားရန်"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"နောက်ထပ်"</string>
+    <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) -->
+    <skip />
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"မည်သို့ပင်ဖြစ်စေ ခွင့်မပြုပါ"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ပယ်ရန်"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> ထဲမှ <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
@@ -186,6 +194,10 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"မီဒီယာကိုသာ သုံးခွင့်ပြုရန်"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"အမြဲ ခွင့်ပြုရန်"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"အက်ပ်ကိုသုံးစဉ်သာ ခွင့်ပြုရန်"</string>
+    <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) -->
+    <skip />
+    <!-- no translation found for app_permission_button_select_photos (1022930616634145364) -->
+    <skip />
     <string name="app_permission_button_ask" msgid="3342950658789427">"အမြဲမေးရန်"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"ခွင့်မပြုပါ"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"နေရာအတိအကျ"</string>
@@ -468,6 +480,8 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"စက်ရှိ &lt;b&gt;ဓာတ်ပုံ၊ ဗီဒီယို၊ တေးဂီတ၊ အသံနှင့်အခြားဖိုင်များ&lt;/b&gt; ကို &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား သုံးခွင့်ပေးမလား။"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"ဤစက်ရှိ တေးဂီတနှင့် အသံဖိုင်ကို &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား သုံးခွင့်ပေးမလား။"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"ဤစက်ရှိ ဓာတ်ပုံနှင့် ဗီဒီယိုများကို &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား သုံးခွင့်ပေးမလား။"</string>
+    <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) -->
+    <skip />
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို အသံဖမ်းယူခွင့် ပေးလိုပါသလား။"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ဤအက်ပ်ကို အသုံးပြုနေသည့် အချိန်တွင်သာ ၎င်းက အသံဖမ်းနိုင်သည်။"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို အသံဖမ်းခွင့် ပေးလိုပါသလား။"</string>
@@ -578,4 +592,6 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"အက်ပ်များက သင်မိတ္တူကူးထားသော စာသား၊ ပုံများ (သို့) အခြားအကြောင်းအရာကို သုံးသောအခါ အကြောင်းကြားပါ"</string>
     <string name="show_password_title" msgid="2877269286984684659">"စကားဝှက်များပြရန်"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"စာရိုက်သည့်အခါ အက္ခရာများကို ခဏတာပြသည်"</string>
+    <!-- no translation found for permission_rationale_message_template (4497650516269082051) -->
+    <skip />
 </resources>
diff --git a/PermissionController/res/values-nb/strings.xml b/PermissionController/res/values-nb/strings.xml
index d472c25..ff8a2f9 100644
--- a/PermissionController/res/values-nb/strings.xml
+++ b/PermissionController/res/values-nb/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Behold «Mens appen er i bruk»"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Behold «Bare denne gangen»"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mer info"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Gi tilgang til alle bilder"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Velg bilder"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Velg flere bilder"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Ikke velg flere bilder"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ikke tillat likevel"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Avvis"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> av <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Har bare tilgang til medier"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Tillat hele tiden"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Bare tillat når appen brukes"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Tillat alle bilder"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Tillat valgte bilder"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Spør hver gang"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Ikke tillat"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Nøyaktig posisjon"</string>
@@ -279,7 +285,7 @@
     <string name="auto_revoke_preference_summary" msgid="5517958331781391481">"Tillatelser er fjernet for å ivareta personvernet ditt"</string>
     <string name="background_location_access_reminder_notification_title" msgid="1140797924301941262">"<xliff:g id="APP_NAME">%s</xliff:g> fikk posisjonen din i bakgrunnen"</string>
     <string name="background_location_access_reminder_notification_content" msgid="7787084707336546245">"Denne appen har alltid tilgang til posisjonen din. Trykk for å endre."</string>
-    <string name="notification_listener_reminder_notification_title" msgid="3747210460187479091">"Gjennomgå apper med tilgang til varslene"</string>
+    <string name="notification_listener_reminder_notification_title" msgid="3747210460187479091">"Gjennomgangsapp med tilgang til varslene dine"</string>
     <string name="notification_listener_reminder_notification_content" msgid="831476101108863427">"<xliff:g id="APP_NAME">%s</xliff:g> kan avvise, utføre handlinger basert på og bruke innhold i varslene dine"</string>
     <string name="notification_listener_warning_card_content" msgid="7840973324284115893">"Denne appen kan avvise, utføre handlinger basert på og bruke innhold i varslene dine. Noen apper krever denne tilgangen for å fungere som de skal."</string>
     <string name="notification_listener_remove_access_button_label" msgid="7101898782417817097">"Fjern tilgang"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bruke &lt;b&gt;bilder, videoer, musikk, lyd og andre filer&lt;/b&gt; på denne enheten?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bruke musikk og lyd på denne enheten?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bruke bilder og videoer på denne enheten?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til flere bilder?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ta opp lyd?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Appen kan bare ta opp lyd mens du bruker den."</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ta opp lyd?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Vis en melding når apper bruker tekst, bilder eller annet innhold du har kopiert"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Vis passord"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Vis tegnene et øyeblikk mens du skriver"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Denne appen har oppgitt at den kan dele <xliff:g id="PERMISSION_NAME">%s</xliff:g>-data med tredjeparter"</string>
 </resources>
diff --git a/PermissionController/res/values-ne/strings.xml b/PermissionController/res/values-ne/strings.xml
index ff1abc2..45aab2d 100644
--- a/PermissionController/res/values-ne/strings.xml
+++ b/PermissionController/res/values-ne/strings.xml
@@ -32,6 +32,14 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“एप प्रयोगमा भएको बेलामा” शीर्षक कायम राख्नुहोस्"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“यस बेला मात्र” राख्नुहोस्"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"थप जानकारी"</string>
+    <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) -->
+    <skip />
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"जे भए पनि फेरि नसोध्नुहोस्"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"खारेज गर्नुहोस्"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> मध्ये <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
@@ -186,6 +194,10 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"केवल मिडिया प्रयोग गर्ने अनुमति दिइयोस्"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"सधैँ अनुमति दिइयोस्"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"यो एप प्रयोग गरिरहेका बेला मात्र अनुमति दिइयोस्"</string>
+    <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) -->
+    <skip />
+    <!-- no translation found for app_permission_button_select_photos (1022930616634145364) -->
+    <skip />
     <string name="app_permission_button_ask" msgid="3342950658789427">"प्रत्येक पटक सोधियोस्"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"अनुमति नदिइयोस्"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"सटीक स्थान"</string>
@@ -468,6 +480,8 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई यो डिभाइसमा रहेका &lt;b&gt;फोटो, भिडियो, सङ्गीत, अडियो तथा अन्य फाइलहरू&lt;/b&gt; प्रयोग गर्न दिने हो?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई यस डिभाइसमा रहेका सङ्गीत तथा अन्य अडियो फाइलहरू प्रयोग गर्न दिने हो?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई यस डिभाइसमा रहेका फोटो र भिडियोहरू प्रयोग गर्न दिने हो?"</string>
+    <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) -->
+    <skip />
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई अडियो रेकर्ड गर्न दिने हो?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"तपाईंले यो एप प्रयोग गरिरहेका बेलामा मात्र यसले अडियो रेकर्ड गर्न सक्ने छ"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई अडियो रेकर्ड गर्न दिने हो?"</string>
@@ -578,4 +592,6 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"मैले कपी गरेका टेक्स्ट, फोटो वा अन्य सामग्री एपहरूले प्रयोग गर्दा म्यासेज देखाइयोस्"</string>
     <string name="show_password_title" msgid="2877269286984684659">"पासवर्डहरू देखाइयोस्"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"टाइप गर्दै गर्दा वर्णहरू झलक्क देखाइयोस्"</string>
+    <!-- no translation found for permission_rationale_message_template (4497650516269082051) -->
+    <skip />
 </resources>
diff --git a/PermissionController/res/values-night/themes.xml b/PermissionController/res/values-night/themes.xml
index b4257ab..7ac9b19 100644
--- a/PermissionController/res/values-night/themes.xml
+++ b/PermissionController/res/values-night/themes.xml
@@ -40,5 +40,5 @@
         <item name="android:background">@color/divider_color_secondary</item>
     </style>
 
-    <style name="Theme.DeviceDefault.Dialog.Alert.DayNight" parent="@android:style/Theme.DeviceDefault.Dialog.Alert" />
+    <style name="Theme.DeviceDefault.Dialog.NoActionBar.DayNight" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar" />
 </resources>
diff --git a/PermissionController/res/values-nl/strings.xml b/PermissionController/res/values-nl/strings.xml
index b797254..70d7b96 100644
--- a/PermissionController/res/values-nl/strings.xml
+++ b/PermissionController/res/values-nl/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Instelling \'Terwijl de app wordt gebruikt\' behouden"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\'Alleen deze keer\' behouden"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Meer informatie"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Toegang geven tot alle foto\'s"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Foto\'s selecteren"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Meer foto\'s selecteren"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Geen foto\'s meer selecteren"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Toch niet toestaan"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Sluiten"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> van <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Alleen toegang tot media toestaan"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Altijd toestaan"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Toestaan bij gebruik van app"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Alle foto\'s toestaan"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Geselecteerde foto\'s toestaan"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Altijd vragen"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Niet toestaan"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Exacte locatie"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot &lt;b&gt;foto\'s, video\'s, muziek, audio en andere bestanden&lt;/b&gt; op dit apparaat?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot muziek en audio op dit apparaat?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot foto\'s en video\'s op dit apparaat?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot meer foto\'s?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om audio op te nemen?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Deze app kan alleen audio opnemen als je de app gebruikt"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om audio op te nemen?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Toon een bericht als apps toegang hebben tot tekst, afbeeldingen of andere content die je hebt gekopieerd"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Wachtwoorden tonen"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Tekens kort tonen terwijl je typt"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Deze app heeft vermeld dat deze <xliff:g id="PERMISSION_NAME">%s</xliff:g>-gegevens kan delen met derden."</string>
 </resources>
diff --git a/PermissionController/res/values-or/strings.xml b/PermissionController/res/values-or/strings.xml
index 9e53e3e..1a36960 100644
--- a/PermissionController/res/values-or/strings.xml
+++ b/PermissionController/res/values-or/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“ଆପଟି ବ୍ୟବହାରରେ ଥିବା ସମୟରେ”କୁ ରଖନ୍ତୁ"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“କେବଳ ଏହି ସମୟ” ରଖନ୍ତୁ"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"ଅଧିକ ସୂଚନା"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"ସମସ୍ତ ଫଟୋକୁ ଆକ୍ସେସ କରିବାର ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ଫଟୋଗୁଡ଼ିକୁ ଚୟନ କରନ୍ତୁ"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"ଅଧିକ ଫଟୋ ଚୟନ କରନ୍ତୁ"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"ଅଧିକ ଫଟୋ ଚୟନ କରନ୍ତୁ ନାହିଁ"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"ଯେ କୌଣସି ମତେ ଅନୁମତି ଦିଅ ନାହିଁ"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ଖାରଜ କରନ୍ତୁ"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> ରୁ <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"କେବଳ ମିଡିଆକୁ ଆକ୍ସେସ୍ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ସବୁ ସମୟ ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"ଆପ୍ ବ୍ୟବହାର ବେଳେ କେବଳ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"ସମସ୍ତ ଫଟୋକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"ଚୟନିତ ଫଟୋଗୁଡ଼ିକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"ପ୍ରତ୍ୟେକ ଥର ପଚାରନ୍ତୁ"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"ସଠିକ୍ ଲୋକେସନ୍"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"ଏହି ଡିଭାଇସରେ ଥିବା&lt;b&gt;ଫଟୋ, ଭିଡିଓ, ମ୍ୟୁଜିକ, ଅଡିଓ ଓ ଅନ୍ୟ ଫାଇଲ&lt;/b&gt; ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"ଏହି ଡିଭାଇସରେ ଥିବା ମ୍ୟୁଜିକ ଏବଂ ଅଡିଓକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"ଏହି ଡିଭାଇସରେ ଥିବା ଫଟୋ ଏବଂ ଭିଡିଓଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"ଅଧିକ ଫଟୋକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅଡିଓ ରେକର୍ଡ କରିବା ପାଇଁ ଅନୁମତି ଦେବେ କି?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ଆପଣ ଆପକୁ ବ୍ୟବହାର କରୁଥିବା ସମୟରେ କେବଳ ଏହା ଅଡିଓ ରେକର୍ଡ କରିବାକୁ ସକ୍ଷମ ହେବ"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅଡିଓ ରେକର୍ଡ କରିବାକୁ ଅନୁମତି ଦେବେ କି?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"ଯେତେବେଳେ ଆପଣ କପି କରିଥିବା ଟେକ୍ସଟ, ଇମେଜ କିମ୍ବା ଅନ୍ୟ ବିଷୟବସ୍ତୁକୁ ଆପ୍ସ ଆକ୍ସେସ କରେ, ସେତେବେଳେ ଏକ ମେସେଜ ଦେଖାନ୍ତୁ"</string>
     <string name="show_password_title" msgid="2877269286984684659">"ପାସୱାର୍ଡଗୁଡ଼ିକ ଦେଖାନ୍ତୁ"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"ଆପଣ ଟାଇପ କରିବା ସମୟରେ କେରେକ୍ଟରଗୁଡ଼ିକୁ କିଛି ସମୟ ପାଇଁ ଡିସପ୍ଲେ କରନ୍ତୁ"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"ଏହି ଆପ ଉଲ୍ଲେଖ କରିଛି ଯେ ଏହା ତୃତୀୟ ପକ୍ଷଗୁଡ଼ିକ ସହ <xliff:g id="PERMISSION_NAME">%s</xliff:g> ଡାଟା ସେୟାର କରିପାରେ"</string>
 </resources>
diff --git a/PermissionController/res/values-pa/strings.xml b/PermissionController/res/values-pa/strings.xml
index 4904cb3..c255f12 100644
--- a/PermissionController/res/values-pa/strings.xml
+++ b/PermissionController/res/values-pa/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“ਐਪ ਵਰਤੋਂ ਵਿੱਚ ਹੋਣ ਵੇਲੇ” ਨੂੰ ਰੱਖੋ"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“ਸਿਰਫ਼ ਇਸ ਸਮੇਂ ਲਈ ਇਜਾਜ਼ਤ ਦਿਓ” ਰੱਖੋ"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"ਹੋਰ ਜਾਣਕਾਰੀ"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"ਸਾਰੀਆਂ ਫ਼ੋਟੋਆਂ ਤੱਕ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ਫ਼ੋਟੋਆਂ ਚੁਣੋ"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"ਹੋਰ ਫ਼ੋਟੋਆਂ ਚੁਣੋ"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"ਹੋਰ ਫ਼ੋਟੋਆਂ ਨਾ ਚੁਣੋ"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"ਫਿਰ ਵੀ ਆਗਿਆ ਨਾ ਦਿਓ"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ਖਾਰਜ ਕਰੋ"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> ਵਿੱਚੋਂ <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"ਸਿਰਫ਼ ਮੀਡੀਆ ਲਈ ਪਹੁੰਚ ਕਰਨ ਦਿਓ"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ਹਰ ਵੇਲੇ ਕਰਨ ਦਿਓ"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"ਸਿਰਫ਼ ਐਪ ਵਰਤੇ ਜਾਣ ਵੇਲੇ ਕਰਨ ਦਿਓ"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"ਸਾਰੀਆਂ ਫ਼ੋਟੋਆਂ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"ਚੋਣਵੀਆਂ ਫ਼ੋਟੋਆਂ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"ਹਰ ਵਾਰ ਪੁੱਛੋ"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"ਨਾ ਕਰਨ ਦਿਓ"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"ਸਹੀ ਟਿਕਾਣਾ"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਇਸ ਡੀਵਾਈਸ \'ਤੇ &lt;b&gt;ਫ਼ੋਟੋਆਂ, ਵੀਡੀਓ, ਸੰਗੀਤ, ਆਡੀਓ ਅਤੇ ਹੋਰ ਫ਼ਾਈਲਾਂ&lt;/b&gt; ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਸੰਗੀਤ ਅਤੇ ਆਡੀਓ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਫ਼ੋਟੋਆਂ ਅਤੇ ਵੀਡੀਓ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਹੋਰ ਫ਼ੋਟੋਆਂ ਤੱਕ ਪਹੁੰਚ ਦੇਣੀ ਹੈ?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੇਣੀ ਹੈ?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਐਪ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵੇਲੇ ਹੀ ਐਪ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰ ਸਕੇਗੀ"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣੀ ਹੈ?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"ਜਦੋਂ ਐਪਾਂ ਲਿਖਤ, ਚਿੱਤਰ ਜਾਂ ਤੁਹਾਡੇ ਵੱਲੋਂ ਕਾਪੀ ਕੀਤੀ ਹੋਰ ਸਮੱਗਰੀ ਤੱਕ ਪਹੁੰਚ ਕਰਦੀਆਂ ਹਨ, ਤਾਂ ਕੋਈ ਸੁਨੇਹਾ ਦਿਖਾਓ"</string>
     <string name="show_password_title" msgid="2877269286984684659">"ਪਾਸਵਰਡ ਦਿਖਾਓ"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"ਟਾਈਪ ਕਰਨ ਵੇਲੇ ਅੱਖਰ-ਚਿੰਨ੍ਹਾਂ ਨੂੰ ਥੋੜ੍ਹੇ ਸਮੇਂ ਲਈ ਦਿਖਾਓ"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"ਇਸ ਐਪ ਨੇ ਸਪਸ਼ਟ ਕੀਤਾ ਕਿ ਇਹ ਤੀਜੀਆਂ ਧਿਰਾਂ ਨਾਲ <xliff:g id="PERMISSION_NAME">%s</xliff:g> ਡਾਟੇ ਨੂੰ ਸਾਂਝਾ ਕਰ ਸਕਦੀ ਹੈ"</string>
 </resources>
diff --git a/PermissionController/res/values-pl/strings.xml b/PermissionController/res/values-pl/strings.xml
index 87507a9..52f14e1 100644
--- a/PermissionController/res/values-pl/strings.xml
+++ b/PermissionController/res/values-pl/strings.xml
@@ -24,7 +24,7 @@
     <string name="available" msgid="6007778121920339498">"Odblokowany"</string>
     <string name="blocked" msgid="9195547604866033708">"Zablokowany"</string>
     <string name="on" msgid="280241003226755921">"Włączono"</string>
-    <string name="off" msgid="1438489226422866263">"Wyłącz"</string>
+    <string name="off" msgid="1438489226422866263">"Wyłączono"</string>
     <string name="uninstall_or_disable" msgid="4496612999740858933">"Odinstaluj lub wyłącz"</string>
     <string name="app_not_found_dlg_title" msgid="6029482906093859756">"Nie znaleziono aplikacji"</string>
     <string name="grant_dialog_button_deny" msgid="88262611492697192">"Nie zezwalaj"</string>
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Zachowaj „Podczas używania aplikacji”"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Zachowaj „Tylko tym razem”"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Więcej"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Zezwól na dostęp do wszystkich zdjęć"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Wybierz zdjęcia"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Wybierz więcej zdjęć"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Nie wybieraj kolejnych zdjęć"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"I tak nie zezwalaj"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Odrzuć"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> z <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Pozwól na dostęp tylko do multimediów"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Zawsze zezwalaj"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Zezwalaj tylko podczas używania aplikacji"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Zezwól na wszystkie zdjęcia"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Zezwól na wybrane zdjęcia"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Zawsze pytaj"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Nie zezwalaj"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Dokładna lokalizacja"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do zdjęć, filmów, muzyki, dźwięków i innych plików na tym urządzeniu?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do muzyki i innych plików audio na tym urządzeniu?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do zdjęć i filmów na tym urządzeniu?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Przyznać aplikacji „<xliff:g id="APP_NAME">%1$s</xliff:g>” dostęp do kolejnych zdjęć?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na nagrywanie dźwięku?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacja będzie mogła nagrywać dźwięk tylko wtedy, gdy będzie używana"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Zezwolić aplikacji „<xliff:g id="APP_NAME">%1$s</xliff:g>” na nagrywanie dźwięku?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Pokazuj komunikat, gdy aplikacja uzyskuje dostęp do skopiowanego tekstu, obrazów lub innych treści"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Pokazuj hasła"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Wpisywane znaki są przez chwilę wyświetlane"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Ta aplikacja deklaruje, że może udostępniać dane typu <xliff:g id="PERMISSION_NAME">%s</xliff:g> osobom trzecim"</string>
 </resources>
diff --git a/PermissionController/res/values-pt-rBR/strings.xml b/PermissionController/res/values-pt-rBR/strings.xml
index 6b1cdbc..a5d16da 100644
--- a/PermissionController/res/values-pt-rBR/strings.xml
+++ b/PermissionController/res/values-pt-rBR/strings.xml
@@ -32,6 +32,14 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Manter \"Enquanto o app estiver em uso\""</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Manter \"Apenas esta vez\""</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mais inform."</string>
+    <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) -->
+    <skip />
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Não permitir mesmo assim"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Dispensar"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +194,10 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Permitir acesso apenas a arquivos de mídia"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Permitir o tempo todo"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Permitir durante o uso do app"</string>
+    <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) -->
+    <skip />
+    <!-- no translation found for app_permission_button_select_photos (1022930616634145364) -->
+    <skip />
     <string name="app_permission_button_ask" msgid="3342950658789427">"Perguntar sempre"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Não permitir"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Local exato"</string>
@@ -468,6 +480,8 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse &lt;b&gt;fotos, vídeos, músicas, áudios e outros arquivos&lt;/b&gt; neste dispositivo?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse músicas e áudios neste dispositivo?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse fotos e vídeos neste dispositivo?"</string>
+    <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) -->
+    <skip />
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"O app poderá gravar áudio apenas quando estiver em uso"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio?"</string>
@@ -578,4 +592,6 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Mostrar uma mensagem quando os apps acessarem textos, imagens ou outros conteúdos copiados"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Mostrar senhas"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Mostrar os caracteres rapidamente enquanto você digita"</string>
+    <!-- no translation found for permission_rationale_message_template (4497650516269082051) -->
+    <skip />
 </resources>
diff --git a/PermissionController/res/values-pt-rPT/strings.xml b/PermissionController/res/values-pt-rPT/strings.xml
index de95810..3802649 100644
--- a/PermissionController/res/values-pt-rPT/strings.xml
+++ b/PermissionController/res/values-pt-rPT/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Manter \"Enquanto a app está a ser utilizada”"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Manter “Apenas desta vez”"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mais informação"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Permitir acesso a todas as fotos"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Selecionar fotos"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Selecionar mais fotos"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Não selecionar mais fotos"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Não permitir mesmo assim"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Ignorar"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Permitir apenas o acesso ao conteúdo multimédia"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Permitir sempre"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Permitir apenas enquanto uso a app"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Permita todas as fotos"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Permita fotos selecionadas"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Perguntar sempre"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Não permitir"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Localização exata"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda a &lt;b&gt;fotos, vídeos, música, áudio, etc.&lt;/b&gt; neste dispositivo?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda a música e áudio neste dispositivo?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda a fotos e vídeos neste dispositivo?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Concede à app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesso a mais fotos?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"A app apenas poderá gravar áudio enquanto a estiver a utilizar."</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Apresente uma mensagem quando as apps acedem a texto, imagens ou outro conteúdo que copiou"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Mostrar palavras-passe"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Apresente rapidamente os carateres ao escrever"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Esta app declarou que pode partilhar dados da autorização <xliff:g id="PERMISSION_NAME">%s</xliff:g> com terceiros"</string>
 </resources>
diff --git a/PermissionController/res/values-pt/strings.xml b/PermissionController/res/values-pt/strings.xml
index 6b1cdbc..a5d16da 100644
--- a/PermissionController/res/values-pt/strings.xml
+++ b/PermissionController/res/values-pt/strings.xml
@@ -32,6 +32,14 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Manter \"Enquanto o app estiver em uso\""</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Manter \"Apenas esta vez\""</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mais inform."</string>
+    <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) -->
+    <skip />
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Não permitir mesmo assim"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Dispensar"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> de <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +194,10 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Permitir acesso apenas a arquivos de mídia"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Permitir o tempo todo"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Permitir durante o uso do app"</string>
+    <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) -->
+    <skip />
+    <!-- no translation found for app_permission_button_select_photos (1022930616634145364) -->
+    <skip />
     <string name="app_permission_button_ask" msgid="3342950658789427">"Perguntar sempre"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Não permitir"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Local exato"</string>
@@ -468,6 +480,8 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse &lt;b&gt;fotos, vídeos, músicas, áudios e outros arquivos&lt;/b&gt; neste dispositivo?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse músicas e áudios neste dispositivo?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse fotos e vídeos neste dispositivo?"</string>
+    <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) -->
+    <skip />
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"O app poderá gravar áudio apenas quando estiver em uso"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio?"</string>
@@ -578,4 +592,6 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Mostrar uma mensagem quando os apps acessarem textos, imagens ou outros conteúdos copiados"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Mostrar senhas"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Mostrar os caracteres rapidamente enquanto você digita"</string>
+    <!-- no translation found for permission_rationale_message_template (4497650516269082051) -->
+    <skip />
 </resources>
diff --git a/PermissionController/res/values-ro/strings.xml b/PermissionController/res/values-ro/strings.xml
index aab56e5..ece525e 100644
--- a/PermissionController/res/values-ro/strings.xml
+++ b/PermissionController/res/values-ro/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Păstrează opțiunea „Când aplicația este folosită”"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Păstrează „Doar de data aceasta”"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mai multe info."</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Permite accesul la toate fotografiile"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Selectează fotografii"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Selectează mai multe fotografii"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Nu selecta mai multe fotografii"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Nu permite în nicio situație"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Închide"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> din <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Permite accesul numai la fișierele media"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Permite întotdeauna"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Permite numai când folosești aplicația"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Permite toate fotografiile"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Permite fotografiile selectate"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Întreabă de fiecare dată"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Nu permite"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Locația exactă"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Permiți accesul &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; la &lt;b&gt;fotografii, clipuri, conținut audio, muzică și alte fișiere&lt;/b&gt; de pe dispozitiv?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Permiți accesul &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; la muzică și fișiere audio de pe acest dispozitiv?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze fotografiile și videoclipurile de pe dispozitiv?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Permiți accesul &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; la mai multe fotografii?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să înregistreze audio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplicația va putea să înregistreze conținut audio doar când o folosești"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să înregistreze audio?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Afișează un mesaj când aplicațiile accesează text, imagini sau alte tipuri de conținut pe care le-ai copiat"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Afișează parolele"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Caracterele se afișează pentru scurt timp, pe măsură ce tastezi"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Aplicația afirmă că poate trimite terților date despre <xliff:g id="PERMISSION_NAME">%s</xliff:g>"</string>
 </resources>
diff --git a/PermissionController/res/values-ru/strings.xml b/PermissionController/res/values-ru/strings.xml
index b84c9e7..e6dee78 100644
--- a/PermissionController/res/values-ru/strings.xml
+++ b/PermissionController/res/values-ru/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Оставить доступ только в активном режиме"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Оставить \"Только в этот раз\""</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Подробнее"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Разрешить доступ ко всем фото"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Выбрать фото"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Выбрать ещё фото"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Больше не выбирать фото"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Все равно запретить"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Закрыть"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> из <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Разрешить доступ только к медиафайлам"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Разрешить в любом режиме"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Разрешить только во время использования приложения"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Разрешить все фото"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Разрешить выбранные фото"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Спрашивать каждый раз"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Запретить"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Точное местоположение"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к &lt;b&gt;фото, видео, аудио и другим файлам&lt;/b&gt; на устройстве?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к музыке и аудио на устройстве?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к фото и видео на устройстве?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Предоставить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к другим фотографиям?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; записывать аудио?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Приложение будет записывать аудио, только когда вы им пользуетесь."</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; записывать аудио?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Показывать уведомления, когда приложения обращаются к скопированному тексту, изображениям или другому контенту"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Показывать пароли"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Ненадолго показывать символы при вводе"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Это приложение может передавать третьим лицам данные, относящиеся к категории \"<xliff:g id="PERMISSION_NAME">%s</xliff:g>\""</string>
 </resources>
diff --git a/PermissionController/res/values-si/strings.xml b/PermissionController/res/values-si/strings.xml
index 8c9361a..8e4a7d3 100644
--- a/PermissionController/res/values-si/strings.xml
+++ b/PermissionController/res/values-si/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“යෙදුම භාවිතයේ තිබෙන අතරතුර” තබා ගන්න"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“මේ වතාවේ පමණක්” තබා ගන්න"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"තවත් තතු"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"සියලු ඡායාරූප වෙත ප්‍රවේශ වීමට ඉඩ දෙන්න"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ඡායාරූප තෝරන්න"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"තවත් ඡායාරූප තෝරන්න"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"තවත් ඡායාරූප තෝරන්න එපා"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"කෙසේ වෙතත් ඉඩ නොදෙන්න"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ඉවත ලන්න"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>කින් <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"මාධ්‍ය වෙත ප්‍රවේශය පමණක් ඉඩ දෙන්න"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"සැම විටම ඉඩ දෙන්න"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"යෙදුම භාවිතයේදී පමණක් ඉඩ දෙන්න"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"ඡායාරූප සියල්ලට අවසර දෙන්න"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"තෝරන ලද ඡායාරූපවලට අවසර දෙන්න"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"සෑම විටම ඉල්ලන්න"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"ඉඩ නොදෙන්න"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"ඉතා නිවැරදි ස්ථානය"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; හට මෙම උපාංගයේ &lt;b&gt;ඡායාරූප, වීඩියෝ, සංගීතය, ශ්‍රව්‍ය සහ වෙනත් ගොනු&lt;b&gt; වෙත ප්‍රවේශ වීමට ඉඩ දෙන්නද?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; හට මෙම උපාංගයේ සංගීතය සහ ශ්‍රව්‍ය වෙත ප්‍රවේශ වීමට ඉඩ දෙන්නද?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; හට මෙම උපාංගයේ ඇති ඡායාරූප සහ වීඩියෝ වෙත ප්‍රවේශ වීමට ඉඩ දෙන්නද?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට තවත් ඡායාරූප වෙත ප්‍රවේශය දෙන්නද?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; වෙත ශබ්දය පටි ගත කිරීමට ඉඩ දෙන්නද?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"යෙදුමට ඔබ යෙදුම භාවිත කරන අතරතුර ඕඩියෝ පටිගත කිරීමට පමණක් හැකි වනු ඇත"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට ඕඩියෝ පටිගත කිරීමට ඉඩ දෙන්නද?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"යෙදුම් ඔබ පිටපත් කර ඇති පාඨ, රූප හෝ වෙනත් අන්තර්ගතය වෙත ප්‍රවේශ වන විට පණිවුඩයක් පෙන්වන්න"</string>
     <string name="show_password_title" msgid="2877269286984684659">"මුරපද පෙන්වන්න"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"ඔබ ටයිප් කරන විට අනුලකුණු කෙටියෙන් පෙන්වන්න"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"මෙම යෙදුම තෙවන පාර්ශව සමග <xliff:g id="PERMISSION_NAME">%s</xliff:g> දත්ත බෙදා ගත හැකි යැයි මෙය සඳහන් කළා"</string>
 </resources>
diff --git a/PermissionController/res/values-sk/strings.xml b/PermissionController/res/values-sk/strings.xml
index d5cd432..6ecde85 100644
--- a/PermissionController/res/values-sk/strings.xml
+++ b/PermissionController/res/values-sk/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Ponechať Počas používania aplikácie"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Ponechať možnosť Iba tentokrát"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Ďalšie info"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Povoliť prístup k všetkým fotkám"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Vybrať fotky"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Vybrať ďalšie fotky"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Nevyberať ďalšie fotky"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Aj tak nepovoliť"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Zavrieť"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> z <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Povoliť prístup iba k médiám"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Povoliť vždy"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Povoliť iba pri používaní aplikácie"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Povoliť všetky fotky"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Povoliť vybrané fotky"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Vždy sa opýtať"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Nepovoliť"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Presná poloha"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k &lt;b&gt;fotkám, videám, hudbe, zvuku a ďalším súborom&lt;/b&gt; v tomto zariadení?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k hudbe a zvuku v tomto zariadení?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k fotkám a videám v tomto zariadení?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Chcete udeliť aplikácii <xliff:g id="APP_NAME">%1$s</xliff:g> prístup k ďalším fotkám?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nahrávať zvuk?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Táto aplikácia bude môcť nahrávať zvuk iba vtedy, keď ju budete používať"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nahrávať zvuk?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Zobrazovať správu, keď sa aplikácie získajú pristup k textu, obrázkom alebo inému obsahu, ktorý ste skopírovali"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Zobrazovať heslá"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Pri písaní nakrátko zobrazovať zadávané znaky"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Táto aplikácia uvádzala, že môže zdieľať údaje povolenia <xliff:g id="PERMISSION_NAME">%s</xliff:g> s tretími stranami"</string>
 </resources>
diff --git a/PermissionController/res/values-sl/strings.xml b/PermissionController/res/values-sl/strings.xml
index d3e526d..ef2e75e 100644
--- a/PermissionController/res/values-sl/strings.xml
+++ b/PermissionController/res/values-sl/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Obdrži »Ko je aplikacija v uporabi«"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Obdrži »Samo tokrat«"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Več informacij"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Dovoli dostop do vseh fotografij"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Izbira fotografij"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Izbira več fotografij"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Brez izbire več fotografij"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ne dovoli kljub temu"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Opusti"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> od <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Dovoli samo dostop do predstavnosti"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Vedno dovoli"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Dovoli samo med uporabo aplikacije"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Dovoli vse fotografije"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Dovoli izbrane fotografije"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Vedno vprašaj"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Ne dovoli"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Natančna lokacija"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do &lt;b&gt;fotografij, videoposnetkov, glasbe, zvočnih datotek in drugih datotek&lt;/b&gt; v tej napravi?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do glasbe in zvočnih datotek v tej napravi?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do fotografij in videoposnetkov v tej napravi?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Ali aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovolite dostop do več fotografij?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snemanje zvoka?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacija bo lahko snemala zvok le med vašo uporabo aplikacije."</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; omogočiti snemanje zvoka?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Pokaži sporočilo, ko aplikacije dostopijo do besedila, slik ali drugih vsebin, ki ste jih kopirali."</string>
     <string name="show_password_title" msgid="2877269286984684659">"Pokaži gesla"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Za trenutek prikaži znake med vnašanjem."</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Ta aplikacija navaja, da bo podatke o dovoljenju »<xliff:g id="PERMISSION_NAME">%s</xliff:g>« morda delila s tretjimi osebami."</string>
 </resources>
diff --git a/PermissionController/res/values-sq/strings.xml b/PermissionController/res/values-sq/strings.xml
index 300a2db..33f5758 100644
--- a/PermissionController/res/values-sq/strings.xml
+++ b/PermissionController/res/values-sq/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Mbaje \"Kur aplikacioni është në përdorim\""</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Mbaje “Vetëm këtë herë”"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Më shumë info."</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Lejo qasjen te të gjitha fotografitë"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Zgjidh fotografitë"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Zgjidh fotografi të tjera"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Mos zgjidh fotografi të tjera"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Mos lejo gjithsesi"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Hiqe"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> nga <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Lejo qasjen vetëm në media"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Lejo gjithmonë"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Lejo vetëm kur përdor aplikacionin"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Lejo të gjitha fotografitë"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Lejo fotografitë e zgjedhura"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Pyet çdo herë"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Mos lejo"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Vendndodhja e saktë"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Të lejohet që &lt;b&amp;gt<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te &lt;b&gt;fotografitë, videot, muzika, audioja e të tjera&lt;/b&gt; në pajisje?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te muzika dhe te audioja në këtë pajisje?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te fotografitë dhe videot në këtë pajisje?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"T\'i jepet &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; qasje në fotografi të tjera?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të regjistrojë audio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacioni do të mund të regjistrojë audion vetëm kur ti po e përdor aplikacionin"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të regjistrojë audio?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Shfaq një mesazh kur aplikacionet qasen te tekstet, imazhet ose përmbajtje të tjera që ke kopjuar"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Shfaq fjalëkalimet"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Shfaq karakteret shkurtimisht kur shkruan"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Ky aplikacion deklaroi se mund të ndajë të dhënat e \"<xliff:g id="PERMISSION_NAME">%s</xliff:g>\" me palë të treta"</string>
 </resources>
diff --git a/PermissionController/res/values-sr/strings.xml b/PermissionController/res/values-sr/strings.xml
index a3480b0..fd4e1a4 100644
--- a/PermissionController/res/values-sr/strings.xml
+++ b/PermissionController/res/values-sr/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Задржи „Док се апликација користи“"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Задржи Само овај пут"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Више информација"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Дозволи приступ свим сликама"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Изаберите слике"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Изаберите још слика"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Немојте да бирате више слика"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ионако не дозволи"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Одбаци"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> од <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Дозволи само приступ медијима"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Дозволи увек"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Дозв. само док се апл. користи"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Дозволи све слике"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Дозволи изабране слике"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Питај сваки пут"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Не дозволи"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Прецизна локација"</string>
@@ -348,7 +354,7 @@
     <string name="role_assistant_short_label" msgid="3369003713187703399">"Апликација дигиталног помоћника"</string>
     <string name="role_assistant_description" msgid="6622458130459922952">"Апликације за помоћ могу да вам помогну на основу информација са екрана који гледате. Неке апликације подржавају услуге покретача и гласовног уноса да би вам пружиле интегрисану помоћ."</string>
     <string name="role_browser_label" msgid="2877796144554070207">"Подразумевана апл. прегледача"</string>
-    <string name="role_browser_short_label" msgid="6745009127123292296">"Апликација за прегледач"</string>
+    <string name="role_browser_short_label" msgid="6745009127123292296">"Апликација прегледача"</string>
     <string name="role_browser_description" msgid="3465253637499842671">"Апликације које вам дају приступ интернету и приказују линкове које можете да додирнете"</string>
     <string name="role_browser_request_title" msgid="2895200507835937192">"Желите ли да подесите <xliff:g id="APP_NAME">%1$s</xliff:g> као подразумевану апликацију за прегледање?"</string>
     <string name="role_browser_request_description" msgid="5888803407905985941">"Није потребна ниједна дозвола"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Приступ сликама, видеу, музици, звуку и другом на уређају за &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Дозвољавате ли приступ музици и звуку на овом уређају за &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Дозвољавате ли приступ сликама и видеу на овом уређају за &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Желите ли да апликацији &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; дозволите приступ већем броју слика?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Желите да дозволите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; снима звук?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Апликација ће моћи да снима звук само док користите апликацију"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Желите да дозволите да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; снима звук?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Приказује поруку када апликације приступају тексту, сликама или другом садржају који сте копирали"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Приказуј лозинке"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Приказује знакове накратко док куцате"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Ова апликација наводи да може да дели податке (<xliff:g id="PERMISSION_NAME">%s</xliff:g>) са трећим странама"</string>
 </resources>
diff --git a/PermissionController/res/values-sv/strings.xml b/PermissionController/res/values-sv/strings.xml
index 37d6ad5..d1e2ad1 100644
--- a/PermissionController/res/values-sv/strings.xml
+++ b/PermissionController/res/values-sv/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Behåll När appen används"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Behåll Bara den här gången"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Mer info"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Ge åtkomst till alla foton"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Välj foton"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Välj fler foton"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Välj inte fler foton"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Tillåt inte ändå"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Stäng"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> av <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Tillåt endast åtkomst till media"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Tillåt alltid"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Tillåt bara när appen används"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Tillåt alla foton"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Tillåt utvalda foton"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Fråga varje gång"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Tillåt inte"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Exakt plats"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till &lt;b&gt;foton, videor, musik, ljud och andra filer&lt;/b&gt; på enheten?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till musik och ljud på enheten?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till foton och videor på enheten?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till fler foton?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; behörighet att spela in ljud?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Appen kan bara spela in ljud medan du använder den"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; behörighet att spela in ljud?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Visa ett meddelande när appar får åtkomst till text, bilder eller annat som du har kopierat"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Visa lösenord"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Visa tecknen en kort stund medan du skriver"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Appen har angett att den kan dela <xliff:g id="PERMISSION_NAME">%s</xliff:g>-data med tredje part"</string>
 </resources>
diff --git a/PermissionController/res/values-sw/strings.xml b/PermissionController/res/values-sw/strings.xml
index 0c0a836..bc7be14 100644
--- a/PermissionController/res/values-sw/strings.xml
+++ b/PermissionController/res/values-sw/strings.xml
@@ -32,6 +32,14 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Usibadilishe “Wakati programu inatumika”"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Usibadilishe “Wakati huu pekee”"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Maelezo zaidi"</string>
+    <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) -->
+    <skip />
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Usiruhusu hata hivyo"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Ondoa"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> kati ya <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +194,10 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Ruhusu ufikiaji wa maudhui pekee"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Ruhusu kila wakati"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Ruhusu tu unapotumia programu"</string>
+    <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) -->
+    <skip />
+    <!-- no translation found for app_permission_button_select_photos (1022930616634145364) -->
+    <skip />
     <string name="app_permission_button_ask" msgid="3342950658789427">"Uliza kila wakati"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Usiruhusu"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Eneo mahususi"</string>
@@ -468,6 +480,8 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie &lt;b&gt;picha, video, muziki, sauti na faili zingine&lt;/b&gt; kwenye kifaa hiki?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie muziki na sauti kwenye kifaa hiki?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie picha na video kwenye kifaa hiki?"</string>
+    <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) -->
+    <skip />
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; kurekodi sauti?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Programu itaweza kurekodi sauti unapoitumia tu"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; irekodi sauti?"</string>
@@ -578,4 +592,6 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Onyesha ujumbe programu zinapofikia maandishi, picha au maudhui mengine uliyonakili"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Onyesha manenosiri"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Onyesha herufi kwa muda mfupi unapoandika"</string>
+    <!-- no translation found for permission_rationale_message_template (4497650516269082051) -->
+    <skip />
 </resources>
diff --git a/PermissionController/res/values-ta/strings.xml b/PermissionController/res/values-ta/strings.xml
index bdbea9a..0ff082c 100644
--- a/PermissionController/res/values-ta/strings.xml
+++ b/PermissionController/res/values-ta/strings.xml
@@ -32,6 +32,14 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“ஆப்ஸ் உபயோகத்தில் இருக்கும்போது” என்று வைக்கவும்"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"”இப்போது மட்டும்” வைத்திரு"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"மேலும் தகவல்"</string>
+    <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) -->
+    <skip />
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"பரவாயில்லை, அனுமதிக்க வேண்டாம்"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"நிராகரி"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +194,10 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"மீடியா ஃபைல்களை மட்டும் அணுக அனுமதி"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"எப்போதும் அனுமதி"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"ஆப்ஸை உபயோகிக்கும்போது மட்டும் அனுமதி"</string>
+    <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) -->
+    <skip />
+    <!-- no translation found for app_permission_button_select_photos (1022930616634145364) -->
+    <skip />
     <string name="app_permission_button_ask" msgid="3342950658789427">"ஒவ்வொரு முறையும் கேள்"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"அனுமதிக்க வேண்டாம்"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"துல்லியமான இருப்பிடம்"</string>
@@ -468,6 +480,8 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"சாதனத்திலுள்ள &lt;b&gt;படம், வீடியோ, இசை, ஆடியோ &amp; பிற ஃபைல்களின்&lt;/b&gt; அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"இந்தச் சாதனத்திலுள்ள இசை மற்றும் ஆடியோவுக்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"இந்தச் சாதனத்திலுள்ள படங்கள் மற்றும் வீடியோக்களுக்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
+    <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) -->
+    <skip />
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"ஆடியோ ரெக்கார்டு செய்ய &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"இந்த ஆப்ஸை நீங்கள் உபயோகிக்கும்போது மட்டுமே ஆடியோ ரெக்கார்டு செய்யும்"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"ஆடியோ ரெக்கார்டு செய்ய &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
@@ -578,4 +592,6 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"நீங்கள் நகலெடுத்த வார்த்தைகளையோ படங்களையோ பிறவற்றையோ ஆப்ஸ் அணுகும்போது ஓர் அறிவிப்பைக் காட்டும்"</string>
     <string name="show_password_title" msgid="2877269286984684659">"கடவுச்சொற்களைக் காட்டுதல்"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"டைப் செய்யும்போதே எழுத்துகளைச் சற்று நேரம் காட்டும்"</string>
+    <!-- no translation found for permission_rationale_message_template (4497650516269082051) -->
+    <skip />
 </resources>
diff --git a/PermissionController/res/values-te/strings.xml b/PermissionController/res/values-te/strings.xml
index 25d8e37..0c0f957 100644
--- a/PermissionController/res/values-te/strings.xml
+++ b/PermissionController/res/values-te/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“యాప్ వినియోగంలో ఉన్నప్పుడు” నిలిపి ఉంచు"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“కేవలం ఈసారి మాత్రమే” ఇలాగే ఉంచు"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"మరింత సమాచారం"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"ఫోటోలన్నింటికీ యాక్సెస్‌ను అనుమతించండి"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"ఫోటోలను ఎంచుకోండి"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"మరిన్ని ఫోటోలను ఎంచుకోండి"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"మరిన్ని ఫోటోలను ఎంచుకోవద్దు"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"ఏదేమైనా అనుమతించవద్దు"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"విస్మరించు"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> యొక్క <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"మీడియాకు మాత్రమే యాక్సెస్‌ను అనుమతించండి"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ఎల్లప్పుడూ అనుమతించండి"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"యాప్‌ను ఉపయోగిస్తున్నప్పుడు మాత్రమే అనుమతించండి"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"ఫోటోలన్నింటినీ అనుమతించండి"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"ఎంచుకోబడిన ఫోటోలను అనుమతించండి"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"ప్రతిసారి అడగాలి"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"అనుమతించవద్దు"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"ఖచ్చితమైన లొకేషన్"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"ఈపరికరంలో &lt;b&gt;ఫోటోలు, వీడియోలు, మ్యూజిక్, ఆడియో, ఇతర ఫైళ్ల&lt;/b&gt; యాక్సెస్‌కు &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"ఈ పరికరంలో మ్యూజిక్‌ను, ఆడియోను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌ను అనుమతించాలా?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"ఈ పరికరంలో ఫోటోలను, వీడియోలను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌ను అనుమతించాలా?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌కు మరిన్ని ఫోటోలను యాక్సెస్ చేయడానికి అనుమతి ఇవ్వాలా?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"ఆడియోను రికార్డ్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"మీరు యాప్‌ను ఉపయోగిస్తున్నప్పుడు మాత్రమే ఈ యాప్, ఆడియోను రికార్డ్ చేయగలుగుతుంది"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"ఆడియోను రికార్డ్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"మీరు కాపీ చేసిన టెక్స్ట్, ఇమేజ్‌లను లేదా ఇతర కంటెంట్‌ను యాప్‌లు యాక్సెస్ చేసినప్పుడు మెసేజ్‌ను చూపుతుంది"</string>
     <string name="show_password_title" msgid="2877269286984684659">"పాస్‌వర్డ్‌లను చూపిస్తుంది"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"మీరు టైప్ చేస్తున్నప్పుడు అక్షరాలను క్లుప్తంగా చూపిస్తుంది"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"ఈ యాప్, అది <xliff:g id="PERMISSION_NAME">%s</xliff:g> డేటాను థర్డ్-పార్టీలతో షేర్ చేయవచ్చని పేర్కొంది"</string>
 </resources>
diff --git a/PermissionController/res/values-th/strings.xml b/PermissionController/res/values-th/strings.xml
index c3aaeba..e89a637 100644
--- a/PermissionController/res/values-th/strings.xml
+++ b/PermissionController/res/values-th/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"คงไว้ที่ “เมื่อมีการใช้แอป”"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"คงไว้ที่ “เฉพาะครั้งนี้”"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"ข้อมูลเพิ่มเติม"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"ให้สิทธิ์เข้าถึงรูปภาพทั้งหมด"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"เลือกรูปภาพ"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"เลือกรูปภาพเพิ่มเติม"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"อย่าเลือกรูปภาพเพิ่มเติม"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"ยังคงไม่อนุญาต"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"ปิด"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> จาก <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> รายการ"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"อนุญาตให้เข้าถึงสื่อเท่านั้น"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"อนุญาตตลอด"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"อนุญาตขณะมีการใช้แอปเท่านั้น"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"อนุญาตรูปภาพทั้งหมด"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"อนุญาตรูปภาพที่เลือก"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"ถามทุกครั้ง"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"ไม่อนุญาต"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"ตำแหน่งที่แน่นอน"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึง&lt;b&gt;รูปภาพ วิดีโอ เพลง เสียง และไฟล์อื่นๆ&lt;/b&gt; ในอุปกรณ์นี้ไหม"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงเพลงและเสียงในอุปกรณ์นี้ไหม"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงรูปภาพและวิดีโอในอุปกรณ์นี้ไหม"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"ให้สิทธ์ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงรูปภาพเพิ่มเติมไหม"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; บันทึกเสียงไหม"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"แอปจะบันทึกเสียงได้ในขณะที่คุณใช้แอปอยู่เท่านั้น"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; บันทึกเสียงไหม"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"แสดงข้อความเมื่อแอปเข้าถึงข้อความ รูปภาพ หรือเนื้อหาอื่นๆ ที่คุณคัดลอก"</string>
     <string name="show_password_title" msgid="2877269286984684659">"แสดงรหัสผ่าน"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"แสดงอักขระชั่วครู่ขณะพิมพ์"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"แอปนี้ระบุว่าแอปอาจแชร์ข้อมูล <xliff:g id="PERMISSION_NAME">%s</xliff:g> กับองค์กรบุคคลที่สามของแอป"</string>
 </resources>
diff --git a/PermissionController/res/values-tl/strings.xml b/PermissionController/res/values-tl/strings.xml
index 987f770..ccc67fc 100644
--- a/PermissionController/res/values-tl/strings.xml
+++ b/PermissionController/res/values-tl/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Panatilihin ang “Habang ginagamit ang app”"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Panatilihing “Sa pagkakataong ito lang”"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Higit pang info"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Magbigay ng access sa lahat ng larawan"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Pumili ng mga larawan"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Pumili ng higit pang larawan"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Huwag pumili ng higit pang larawan"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Huwag pa ring payagan"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"I-dismiss"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> sa <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Payagan lang ang pag-access ng media"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Payagan sa lahat ng oras"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Payagan lang habang ginagamit ang app"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Payagan ang lahat ng larawan"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Payagan ang mga piniling larawan"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Magtanong palagi"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Huwag payagan"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Eksaktong lokasyon"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang &lt;b&gt;mga larawan, video, musika, audio, at iba pang file&lt;/b&gt; sa device?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang musika at audio sa device na ito?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang mga larawan at video sa device na ito?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Payagan ang access ng &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sa higit pang larawan?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na mag-record ng audio?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Makakapag-record lang ng audio ang app habang ginagamit mo ang app"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na mag-record ng audio?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Magpakita ng mensahe kapag ina-access ng mga app ang text, mga larawan, o iba pang content na nakopya mo"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Ipakita ang mga password"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Ipakita sandali ang mga character habang nagta-type ka"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Isinaad ng app na ito na puwedeng ibahagi nito ang data ng <xliff:g id="PERMISSION_NAME">%s</xliff:g> sa mga third party"</string>
 </resources>
diff --git a/PermissionController/res/values-tr/strings.xml b/PermissionController/res/values-tr/strings.xml
index b2ba366..9271991 100644
--- a/PermissionController/res/values-tr/strings.xml
+++ b/PermissionController/res/values-tr/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“Uygulama kullanılırken” tut"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\"Yalnızca bu defa\" sakla"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Daha fazla bilgi"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Tüm fotoğraflara erişim izni ver"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Fotoğraf seç"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Daha fazla fotoğraf seç"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Daha fazla fotoğraf seçme"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Yine de izin verme"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Kapat"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Yalnızca medyaya erişim izni ver"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Her zaman izin ver"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Yalnızca uygulama kullanılırken izin ver"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Tüm fotoğraflara izin ver"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Seçilen fotoğraflara izin ver"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Her zaman sor"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"İzin verme"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Tam konum"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; için cihazdaki &lt;b&gt;fotoğraf, video, müzik, ses vb. dosyalara&lt;/b&gt; erişim verilsin mi?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının bu cihazda müzik ve ses dosyalarına erişmesine izin verilsin mi?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının bu cihazdaki fotoğraf ve videolara erişmesine izin verilsin mi?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının daha fazla fotoğrafa erişmesine izin verilsin mi?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının ses kaydetmesine izin verilsin mi?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Bu uygulama, yalnızca kullanıldığı sırada ses kaydedebilir"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının ses kaydetmesine izin verilsin mi?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Uygulamalar kopyaladığınız metne, resimlere veya diğer içeriklere eriştiğinde mesaj gösterilsin."</string>
     <string name="show_password_title" msgid="2877269286984684659">"Şifreleri göster"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Yazarken karakterleri kısa süreliğine göster"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Bu uygulama, <xliff:g id="PERMISSION_NAME">%s</xliff:g> verilerini üçüncü taraflarla paylaşabileceğini belirtti"</string>
 </resources>
diff --git a/PermissionController/res/values-uk/strings.xml b/PermissionController/res/values-uk/strings.xml
index 58c3bbe..8a1b72f 100644
--- a/PermissionController/res/values-uk/strings.xml
+++ b/PermissionController/res/values-uk/strings.xml
@@ -32,6 +32,14 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Дозволяти, лише коли додаток використовується"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Залишити дозвіл \"Лише цього разу\""</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Докладніше"</string>
+    <!-- no translation found for grant_dialog_button_allow_all_photos (3688746146785304900) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_selected_photos (4098620850512492892) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_allow_more_selected_photos (2003524111894640605) -->
+    <skip />
+    <!-- no translation found for grant_dialog_button_dont_allow_more_selected_photos (6811842813929146242) -->
+    <skip />
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Усе одно не дозволяти"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Закрити"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> з <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +194,10 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Надати доступ лише до медіафайлів"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Дозволяти завжди"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Дозволяти, лише коли додаток використовується"</string>
+    <!-- no translation found for app_permission_button_allow_all_photos (914762549054270764) -->
+    <skip />
+    <!-- no translation found for app_permission_button_select_photos (1022930616634145364) -->
+    <skip />
     <string name="app_permission_button_ask" msgid="3342950658789427">"Запитувати щоразу"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Не дозволяти"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Точне місцезнаходження"</string>
@@ -468,6 +480,8 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Надати додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до &lt;b&gt;фото, відео, музики, аудіо й інших файлів&lt;/b&gt; на пристрої?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Надати додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до музики й аудіофайлів на цьому пристрої?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Надати додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до фотографій і відео на цьому пристрої?"</string>
+    <!-- no translation found for permgrouprequest_more_photos (4697813231897226261) -->
+    <skip />
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; записувати аудіо?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Додаток зможе записувати звук, лише коли ви використовуєте його"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; записувати звук?"</string>
@@ -578,4 +592,6 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"З’являтиметься сповіщення, коли будь-який додаток отримуватиме доступ до скопійованого вами тексту, зображень чи іншого контенту"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Показувати паролі"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Ненадовго показувати символи під час введення"</string>
+    <!-- no translation found for permission_rationale_message_template (4497650516269082051) -->
+    <skip />
 </resources>
diff --git a/PermissionController/res/values-ur/strings.xml b/PermissionController/res/values-ur/strings.xml
index cd0633d..9d73f11 100644
--- a/PermissionController/res/values-ur/strings.xml
+++ b/PermissionController/res/values-ur/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"”جب تک ایپ استعمال میں ہے“ رکھیں"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"\"صرف اس وقت\" رکھیں"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"مزید معلومات"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"سبھی تصاویر تک رسائی کی اجازت دیں"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"تصاویر منتخب کریں"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"مزید تصاویر منتخب کریں"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"مزید تصاویر منتخب نہ کریں"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"کسی بھی صورت میں اجازت نہ دیں"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"برخاست کریں"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> میں سے <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"صرف میڈیا تک رسائی کی اجازت دیں"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"ہر وقت اجازت دیں"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"صرف ایپ استعمال کرتے وقت اجازت دیں"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"تمام تصاویر کی اجازت دیں"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"منتخب کردہ تصاویر کی اجازت دیں"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"ہر بار پوچھیں"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"اجازت نہ دیں"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"قطعی مقام"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"‏اس آلے پر &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو &lt;b&gt;تصاویر، ویڈیوز، موسیقی، آڈیو اور دیگر فائلز&lt;/b&gt; تک رسائی کی اجازت دیں؟"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"‏اس آلے پر &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو موسیقی اور آڈیو تک رسائی کی اجازت دیں؟"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"‏اس آلے پر &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو تصاویر اور ویڈیوز تک رسائی کی اجازت دیں؟"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"‏مزید تصاویر کے لیے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو رسائی کی منظوری دیں؟"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آڈیو ریکارڈ کرنے کی اجازت دیں؟"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"جب آپ ایپ استعمال کر رہے ہوں تب ایپ صرف آڈیو ریکارڈ کر پائے گی"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آڈیو ریکارڈ کرنے کی اجازت دیں؟"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"ایپس کے آپ کے کاپی کردہ ٹیکسٹ، تصاویر یا دیگر مواد تک رسائی حاصل کرنے پر پیغام دکھائیں"</string>
     <string name="show_password_title" msgid="2877269286984684659">"پاس ورڈز دکھائیں"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"ٹائپ کرتے وقت حروف کو مختصر طور پر ڈسپلے کریں"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"اس ایپ نے بتایا ہے کہ یہ <xliff:g id="PERMISSION_NAME">%s</xliff:g> کے ڈیٹا کا اشتراک فریقین ثالث کے ساتھ کر سکتی ہے"</string>
 </resources>
diff --git a/PermissionController/res/values-uz/strings.xml b/PermissionController/res/values-uz/strings.xml
index 4d674d4..34c1d37 100644
--- a/PermissionController/res/values-uz/strings.xml
+++ b/PermissionController/res/values-uz/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"“Ilova ishlatilganda” rejimida qolsin"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"“Faqat shu safar” ruxsat berish"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Batafsil"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Barcha suratlarga kirish uchun ruxsat berish"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Surat tanlash"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Yana surat tanlash"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Boshqa surat tanlanmasin"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Baribir rad etilsin"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Yopish"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> / <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Faqat media fayllarga ruxsat"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Har doim ruxsat"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Faqat ilova faolligida ruxsat"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Barcha suratlarga kirish ruxsati"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Tanlangan suratlarga kirish ruxsati"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Har safar soʻralsin"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Rad etish"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Aniq joylashuv"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun ushbu qurilmadagi surat, video, musiqa, audio va fayllarga kirish ruxsati berilsinmi?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun ushbu qurilmadagi musiqa va audio fayllarga kirish ruxsati berilsinmi?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun qurilmadagi rasm va videolarga kirish ruxsati berilsinmi?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasiga boshqa suratlarga kirish uchun ruxsat berilsinmi?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun audio yozib olish ruxsati berilsinmi?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Bu ilova faqat undan foydalanganingizda ovozlarni yozib oladi"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun audio yozib olishga ruxsat berilsinmi?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Ilovalar siz nusxa olgan matn, rasmlar yoki boshqa kontentdan foydalanganda xabar chiqarish"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Parollar ochiq tursin"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Parolni kiritishda belgilar qisqa muddat ochiq turadi"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Bu ilovaning <xliff:g id="PERMISSION_NAME">%s</xliff:g> maʼlumotlarini begonalarga ulashishi mumkinligi xabar berildi"</string>
 </resources>
diff --git a/PermissionController/res/values-v31/styles.xml b/PermissionController/res/values-v31/styles.xml
index 64fc290..7ce3ff6 100644
--- a/PermissionController/res/values-v31/styles.xml
+++ b/PermissionController/res/values-v31/styles.xml
@@ -90,6 +90,14 @@
            parent="@style/PermissionGrantButtonTop"></style>
     <style name="PermissionGrantButtonAllowOneTimeMaterial3"
            parent="@style/PermissionGrantButtonMiddle"></style>
+    <style name="PermissionGrantButtonAllowAllPhotosMaterial3"
+        parent="@style/PermissionGrantButtonTop"></style>
+    <style name="PermissionGrantButtonAllowSelectedPhotosMaterial3"
+        parent="@style/PermissionGrantButtonMiddle"></style>
+    <style name="PermissionGrantButtonAllowMorePhotosMaterial3"
+        parent="@style/PermissionGrantButtonTop"></style>
+    <style name="PermissionGrantButtonDontAllowMorePhotosMaterial3"
+        parent="@style/PermissionGrantButtonBottom"></style>
     <style name="PermissionGrantButtonDenyMaterial3"
            parent="@style/PermissionGrantButtonBottom"></style>
     <style name="PermissionGrantButtonNoUpgradeMaterial3"
diff --git a/PermissionController/res/values-v33/dimens.xml b/PermissionController/res/values-v33/dimens.xml
index e94eac6..66332ed 100644
--- a/PermissionController/res/values-v33/dimens.xml
+++ b/PermissionController/res/values-v33/dimens.xml
@@ -15,7 +15,6 @@
   -->
 
 <resources>
-
     <dimen name="sc_spacing_xxxsmall">2dp</dimen>
     <dimen name="sc_spacing_xxsmall">4dp</dimen>
     <dimen name="sc_spacing_xsmall">8dp</dimen>
@@ -38,9 +37,12 @@
     <dimen name="sc_entry_group_collapsed_padding_bottom">@dimen/sc_spacing_large</dimen>
     <dimen name="sc_card_margin_bottom">@dimen/sc_spacing_xxxlarge</dimen>
 
+    <dimen name="sc_large_screen_button_padding">56dp</dimen>
     <dimen name="sc_icon_button_touch_target_size">48dp</dimen>
     <dimen name="sc_button_corner_radius">12dp</dimen>
     <dimen name="sc_card_corner_radius_large">28dp</dimen>
     <dimen name="sc_card_corner_radius_medium">20dp</dimen>
     <dimen name="sc_card_corner_radius_xsmall">4dp</dimen>
-</resources>
\ No newline at end of file
+
+    <dimen name="sc_qs_max_width">492dp</dimen>
+</resources>
diff --git a/PermissionController/res/values-v33/styles.xml b/PermissionController/res/values-v33/styles.xml
index 87582c8..376b08c 100644
--- a/PermissionController/res/values-v33/styles.xml
+++ b/PermissionController/res/values-v33/styles.xml
@@ -22,9 +22,18 @@
            parent="android:Widget.DeviceDefault">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_gravity">center_horizontal</item>
         <item name="android:clipChildren">false</item>
+      </style>
+
+    <style name="SafetyCenterQsBody">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:orientation">vertical</item>
+        <item name="android:layout_gravity">center_horizontal</item>
     </style>
 
+
     <style name="SafetyCenterLinkText">
         <item name="android:textDirection">locale</item>
         <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
@@ -46,9 +55,9 @@
         <item name="android:layout_height">wrap_content</item>
         <item name="android:layout_marginTop">@dimen/sc_spacing_xxlarge</item>
         <item name="android:textColor">?attr/colorAccentPrimary</item>
-        <item name="android:paddingStart">@dimen/sc_list_margin</item>
+        <item name="android:paddingStart">@dimen/sc_spacing_xxxlarge</item>
         <item name="android:paddingEnd">@dimen/sc_list_margin</item>
-        <item name="android:textSize">16sp</item>
+        <item name="android:textSize">14sp</item>
         <item name="android:lineHeight">24sp</item>
         <item name="android:textAlignment">viewStart</item>
         <item name="android:textAppearance">@style/TextAppearance.SafetyCenter.Medium</item>
@@ -60,11 +69,13 @@
         <item name="android:orientation">vertical</item>
         <item name="android:paddingStart">@dimen/sc_list_margin</item>
         <item name="android:paddingEnd">@dimen/sc_list_margin</item>
+        <item name="android:layout_marginBottom">@dimen/sc_spacing_xsmall</item>
     </style>
 
     <style name="SafetyCenterQsPreferences">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">match_parent</item>
+        <item name="android:layout_marginTop">@dimen/sc_spacing_xsmall</item>
     </style>
 
     <style name="SafetyCenterQsToggleContainer">
@@ -421,6 +432,17 @@
         <item name="layout_constraintEnd_toEndOf">parent</item>
     </style>
 
+    <style name="SafetyCenterIssueAttributionTitle">
+        <item name="android:textAppearance">@style/TextAppearance.SafetyCenter.Body</item>
+        <item name="android:layout_marginTop">@dimen/sc_spacing_xxsmall</item>
+        <item name="android:layout_width">0dp</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginEnd">@dimen/sc_spacing_xxxlarge</item>
+        <item name="layout_constraintTop_toTopOf">parent</item>
+        <item name="layout_constraintStart_toStartOf">parent</item>
+        <item name="layout_constraintEnd_toStartOf">@id/issue_card_dismiss_btn</item>
+    </style>
+
     <style name="SafetyCenterIssueTitle">
         <item name="android:textAppearance">@style/TextAppearance.SafetyCenter.Headline.Issue</item>
         <item name="android:layout_width">0dp</item>
@@ -428,7 +450,7 @@
         <item name="android:layout_marginEnd">@dimen/sc_spacing_xxxlarge</item>
         <item name="layout_constraintHorizontal_bias">0</item>
         <item name="layout_goneMarginEnd">0dp</item>
-        <item name="layout_constraintTop_toTopOf">parent</item>
+        <item name="layout_constraintTop_toBottomOf">@id/issue_card_attribution_title</item>
         <item name="layout_constraintStart_toStartOf">parent</item>
         <item name="layout_constraintEnd_toStartOf">@id/issue_card_dismiss_btn</item>
     </style>
diff --git a/PermissionController/res/values-v34/strings.xml b/PermissionController/res/values-v34/strings.xml
new file mode 100644
index 0000000..09f2e17
--- /dev/null
+++ b/PermissionController/res/values-v34/strings.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Permission Rationale - Start -->
+
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Title message shown for Permission Rationale dialog [CHAR LIMIT=50] -->
+    <string name="permission_rationale_title">More about allowing this access</string>
+
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Title shown for Permission Rationale data sharing purposes section [CHAR LIMIT=50] -->
+    <string name="permission_rationale_purpose_title">Why the app may share data?</string>
+
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Message shown to the user letting them know that data will be shared and for which
+    purposes. It will also display where the information was provided from via the install_source
+    placeholder, this will map to app store labels/names. Purposes will be comma delimited and are
+    one or many of the following: "app functionality", "analytics", "developer communications",
+    "advertising or marketing", "fraud prevention, security, and compliance", "personalization",
+    "account management". Example usage as follows: "This app developer stated to App Store that it
+    may share for app functionality, analytics, developer communications, advertising or marketing,
+    fraud prevention, security, and compliance, personalization, account management. This may be
+    updated in the future." [CHAR LIMIT=300] -->
+    <string name="permission_rationale_purpose_message">This app developer stated to <annotation id="link"><annotation id="install_source" example="App Store">%1$s</annotation></annotation> that it may share for <annotation id="purpose_list" example="purpose 1, purpose 2, purpose 3">%2$s</annotation>\nThis may be updated in the future.</string>
+
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Message shown to the user letting them know that data will be shared and for which
+    purposes. It will also display that the information was provided via the device manufacturer
+    (company responsible for making and released the device). Purposes will be comma delimited and
+    are one or many of the following: "app functionality", "analytics", "developer communications",
+    "advertising or marketing", "fraud prevention, security, and compliance", "personalization",
+    "account management". Example usage as follows: "This app developer stated to the device
+    manufacturer that it may share for app functionality, analytics, developer communications,
+    advertising or marketing, fraud prevention, security, and compliance, personalization, account
+    management. This may be updated in the future." [CHAR LIMIT=300] -->
+    <string name="permission_rationale_purpose_default_source_message">This app developer stated to the device manufacturer that it may share for <xliff:g id="purpose_list" example="purpose 1, purpose 2, purpose 3">%1$s</xliff:g>\nThis may be updated in the future.</string>
+
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Title shown for Permission Rationale third party description section [CHAR LIMIT=50] -->
+    <string name="permission_rationale_thirdparty_title">What are third parties?</string>
+
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Message shown to the user that explains what a "third party" is [CHAR LIMIT=100] -->
+    <string name="permission_rationale_thirdparty_message">Third parties are other companies or organizations</string>
+
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Title shown for Permission Rationale permission settings section [CHAR LIMIT=50] -->
+    <string name="permission_rationale_permission_settings_title">Change or revoke access any time</string>
+
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Message shown to the user letting them where to change permission settings in the future [CHAR LIMIT=100] -->
+    <string name="permission_rationale_permission_settings_message">Go to your <annotation id="link">privacy settings</annotation>, under <xliff:g id="permission_name" example="location">%1$s</xliff:g></string>
+
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Title shown for Permission Rationale "learn more about data sharing" section [CHAR LIMIT=50] -->
+    <string name="permission_rationale_permission_learn_more_title"><annotation id="link">Learn more</annotation> about data sharing</string>
+
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Permission usage purpose shown for Permission Rationale. This will be used with the
+    permission_rationale_purpose_message and permission_rationale_purpose_default_source_message
+    strings. Example usage with this string as follows: "This app developer stated to the device
+    manufacturer that it may share for app functionality. This may be updated in the future."
+    [CHAR LIMIT=30] -->
+    <string name="permission_rational_purpose_app_functionality">app functionality</string>
+
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Permission usage purpose shown for Permission Rationale. This will be used with the
+    permission_rationale_purpose_message and permission_rationale_purpose_default_source_message
+    strings. Example usage with this string as follows: "This app developer stated to the device
+    manufacturer that it may share for analytics. This may be updated in the future."
+    [CHAR LIMIT=30] -->
+    <string name="permission_rational_purpose_analytics">analytics</string>
+
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Permission usage purpose shown for Permission Rationale. This will be used with the
+    permission_rationale_purpose_message and permission_rationale_purpose_default_source_message
+    strings. Example usage with this string as follows: "This app developer stated to the device
+    manufacturer that it may share for developer communications. This may be updated in the future."
+    [CHAR LIMIT=50] -->
+    <string name="permission_rational_purpose_developer_communications">developer communications</string>
+
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Permission usage purpose shown for Permission Rationale. This will be used with the
+    permission_rationale_purpose_message and permission_rationale_purpose_default_source_message
+    strings. Example usage with this string as follows: "This app developer stated to the device
+    manufacturer that it may share for advertising or marketing. This may be updated in the future."
+    [CHAR LIMIT=50] -->
+    <string name="permission_rational_purpose_advertising">advertising or marketing</string>
+
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Permission usage purpose shown for Permission Rationale. This will be used with the
+    permission_rationale_purpose_message and permission_rationale_purpose_default_source_message
+    strings. Example usage with this string as follows: "This app developer stated to the device
+    manufacturer that it may share for fraud prevention, security, and compliance. This may be
+    updated in the future." [CHAR LIMIT=75] -->
+    <string name="permission_rational_purpose_fraud_prevention_security">fraud prevention, security, and compliance</string>
+
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Permission usage purpose shown for Permission Rationale. This will be used with the
+    permission_rationale_purpose_message and permission_rationale_purpose_default_source_message
+    strings. Example usage with this string as follows: "This app developer stated to the device
+    manufacturer that it may share for personalization. This may be updated in the future."
+    [CHAR LIMIT=50] -->
+    <string name="permission_rational_purpose_personalization">personalization</string>
+
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Permission usage purpose shown for Permission Rationale. This will be used with the
+    permission_rationale_purpose_message and permission_rationale_purpose_default_source_message
+    strings. Example usage with this string as follows: "This app developer stated to the device
+    manufacturer that it may share for account management. This may be updated in the future."
+    [CHAR LIMIT=50] -->
+    <string name="permission_rational_purpose_account_management">account management</string>
+
+    <!-- Permission Rationale - End -->
+
+</resources>
diff --git a/PermissionController/res/values-vi/strings.xml b/PermissionController/res/values-vi/strings.xml
index 320a4a8..42e59aa 100644
--- a/PermissionController/res/values-vi/strings.xml
+++ b/PermissionController/res/values-vi/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Duy trì tùy chọn “Khi đang dùng ứng dụng”"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Duy trì tùy chọn “Chỉ lần này”"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Thông tin khác"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Cho phép truy cập vào toàn bộ ảnh"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Chọn ảnh"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Chọn thêm ảnh"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Không chọn thêm ảnh"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Vẫn không cho phép"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Đóng"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Chỉ cho phép truy cập vào nội dung nghe nhìn"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Luôn cho phép"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Chỉ cho phép khi dùng ứng dụng"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Cho phép truy cập vào toàn bộ ảnh"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Cho phép truy cập vào ảnh đã chọn"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Luôn hỏi"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Không cho phép"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Vị trí chính xác"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào &lt;b&gt;ảnh, video, nhạc, âm thanh và các tệp khác&lt;/b&gt; trên thiết bị này?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào nhạc và âm thanh trên thiết bị này?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào ảnh và video trên thiết bị này?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Bạn có đồng ý cấp quyền truy cập thêm ảnh cho &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ghi âm?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Ứng dụng này chỉ có thể ghi âm khi bạn đang dùng ứng dụng"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ghi âm?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Hiện thông báo khi có ứng dụng truy cập vào văn bản, hình ảnh hoặc nội dung khác mà bạn đã sao chép"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Hiển thị mật khẩu"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Hiển thị các ký tự ngắn gọn khi bạn nhập"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Nhà phát triển nêu rõ ứng dụng này có thể chia sẻ dữ liệu <xliff:g id="PERMISSION_NAME">%s</xliff:g> với bên thứ ba"</string>
 </resources>
diff --git a/PermissionController/res/values-w492dp-v33/styles.xml b/PermissionController/res/values-w492dp-v33/styles.xml
new file mode 100644
index 0000000..26d4323
--- /dev/null
+++ b/PermissionController/res/values-w492dp-v33/styles.xml
@@ -0,0 +1,38 @@
+<!--
+  ~ 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.
+  -->
+
+<resources
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+    <!-- START SAFETY CENTER QUICK SETTINGS PAGE -->
+
+    <style name="SafetyCenterQsContainer"
+           parent="android:Widget.DeviceDefault">
+        <!-- Limit width on larger screen devices. -->
+        <item name="android:layout_width">@dimen/sc_qs_max_width</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_gravity">center_horizontal</item>
+        <item name="android:clipChildren">false</item>
+      </style>
+
+    <style name="SafetyCenterQsBody">
+        <!-- Limit width on larger screen devices. -->
+        <item name="android:layout_width">@dimen/sc_qs_max_width</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:orientation">vertical</item>
+        <item name="android:layout_gravity">center_horizontal</item>
+    </style>
+</resources>
diff --git a/PermissionController/res/values-w764dp-v33/styles.xml b/PermissionController/res/values-w764dp-v33/styles.xml
new file mode 100644
index 0000000..02ed655
--- /dev/null
+++ b/PermissionController/res/values-w764dp-v33/styles.xml
@@ -0,0 +1,83 @@
+<!--
+  ~ 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.
+  -->
+
+<resources
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+    <!-- START SAFETY STATUS CARD -->
+    <!-- Overrides to width, constraints and padding to move button location on large screens. -->
+    <style name="SafetyCenterStatusTitleAndSummaryContainer" parent="android:Widget.DeviceDefault">
+        <item name="android:layout_width">0dp</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:orientation">vertical</item>
+        <item name="android:layout_marginTop">@dimen/sc_spacing_xxsmall</item>
+        <item name="android:layout_marginStart">@dimen/sc_spacing_large</item>
+        <item name="android:layout_marginEnd">@dimen/sc_spacing_xxxlarge</item>
+        <item name="layout_constraintStart_toEndOf">@id/status_image</item>
+        <item name="layout_constraintEnd_toStartOf">@id/review_settings_button</item>
+        <item name="layout_constraintTop_toTopOf">parent</item>
+    </style>
+
+    <style name="SafetyCenterStatusButton.ReviewSettings">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="layout_constraintTop_toTopOf">parent</item>
+        <item name="layout_constraintStart_toEndOf">@id/status_title_and_summary</item>
+        <item name="layout_constraintEnd_toStartOf">@id/rescan_button</item>
+        <item name="android:layout_marginTop">@dimen/sc_spacing_xxsmall</item>
+        <item name="android:paddingStart">@dimen/sc_large_screen_button_padding</item>
+        <item name="android:paddingEnd">@dimen/sc_large_screen_button_padding</item>
+        <item name="backgroundTint">@color/safety_center_button_info</item>
+    </style>
+
+    <style name="SafetyCenterStatusButton.Rescan">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="layout_constraintTop_toTopOf">parent</item>
+        <item name="layout_constraintStart_toEndOf">@id/review_settings_button</item>
+        <item name="layout_constraintEnd_toStartOf">@id/pending_actions_rescan_button</item>
+        <item name="android:layout_marginTop">@dimen/sc_spacing_xxsmall</item>
+        <item name="android:paddingStart">@dimen/sc_large_screen_button_padding</item>
+        <item name="android:paddingEnd">@dimen/sc_large_screen_button_padding</item>
+        <item name="backgroundTint">@color/safety_center_button_info</item>
+    </style>
+
+    <style name="SafetyCenterStatusButton.PendingActionsRescan">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="layout_constraintTop_toTopOf">parent</item>
+        <item name="layout_constraintStart_toEndOf">@id/rescan_button</item>
+        <item name="layout_constraintEnd_toEndOf">parent</item>
+        <item name="android:layout_marginTop">@dimen/sc_spacing_xxsmall</item>
+        <item name="android:paddingStart">@dimen/sc_large_screen_button_padding</item>
+        <item name="android:paddingEnd">@dimen/sc_large_screen_button_padding</item>
+        <item name="backgroundTint">@color/sc_surface_dark</item>
+        <item name="strokeWidth">@dimen/mtrl_btn_stroke_size</item>
+        <item name="strokeColor">@color/safety_center_button_info</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="SafetyCenterStatusSafetyProtectionView">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="layout_constraintTop_toBottomOf">@id/status_title_and_summary</item>
+        <item name="layout_constraintStart_toStartOf">parent</item>
+        <item name="layout_constraintEnd_toEndOf">parent</item>
+        <item name="android:layout_gravity">center</item>
+        <item name="android:paddingTop">@dimen/sc_spacing_xxlarge</item>
+        <item name="android:paddingBottom">@dimen/sc_spacing_xxlarge</item>
+    </style>
+</resources>
diff --git a/PermissionController/res/values-w800dp-v33/styles.xml b/PermissionController/res/values-w800dp-v33/styles.xml
new file mode 100644
index 0000000..38ba9d4
--- /dev/null
+++ b/PermissionController/res/values-w800dp-v33/styles.xml
@@ -0,0 +1,38 @@
+<!--
+  ~ 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.
+  -->
+
+<resources
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+    <!-- START SAFETY CENTER QUICK SETTINGS PAGE -->
+
+    <style name="SafetyCenterQsContainer"
+           parent="android:Widget.DeviceDefault">
+        <!-- Make width match_parent (mainly for the close button. -->
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_gravity">center_horizontal</item>
+        <item name="android:clipChildren">false</item>
+      </style>
+
+    <style name="SafetyCenterQsBody">
+        <!-- Continue to limit width on larger screen devices -->
+        <item name="android:layout_width">@dimen/sc_qs_max_width</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_gravity">center_horizontal</item>
+        <item name="android:orientation">vertical</item>
+    </style>
+</resources>
diff --git a/PermissionController/res/values-zh-rCN/strings.xml b/PermissionController/res/values-zh-rCN/strings.xml
index 08e834a..7e142a9 100644
--- a/PermissionController/res/values-zh-rCN/strings.xml
+++ b/PermissionController/res/values-zh-rCN/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"保留“在使用该应用期间”"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"继续使用“仅限这一次”设置"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"更多信息"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"允许访问所有照片"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"选择照片"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"选择更多照片"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"不选择更多照片"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"仍然不允许"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"关闭"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"第 <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> 项权限(共 <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> 项)"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"仅允许访问媒体文件"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"始终允许"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"仅在使用该应用时允许"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"允许访问所有照片"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"允许访问所选照片"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"每次都询问"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"不允许"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"确切位置"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"要允许“&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;”访问此设备上的&lt;b&gt;照片、视频、音乐、音频和其他文件&lt;/b&gt;吗?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;访问此设备上的音乐和音频吗?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;访问此设备上的照片和视频吗?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"要授予<xliff:g id="APP_NAME">%1$s</xliff:g>访问更多照片的权限吗?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;录音吗?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"此应用将只能在您使用它时录音"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;录音吗?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"系统会在应用访问您复制的文字、图片或其他内容时显示一条消息"</string>
     <string name="show_password_title" msgid="2877269286984684659">"显示密码"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"输入时短暂显示字符"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"此应用已声明它可能会与第三方共享<xliff:g id="PERMISSION_NAME">%s</xliff:g>数据"</string>
 </resources>
diff --git a/PermissionController/res/values-zh-rHK/strings.xml b/PermissionController/res/values-zh-rHK/strings.xml
index 6f7d48f..e44f785 100644
--- a/PermissionController/res/values-zh-rHK/strings.xml
+++ b/PermissionController/res/values-zh-rHK/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"保持為「使用應用程式時」"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"保留「只在這次允許」"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"更多資料"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"允許存取所有相片"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"選取相片"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"選取更多相片"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"不選取更多相片"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"一律不允許"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"關閉"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"第 <xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> 個 (共 <xliff:g id="PERMISSION_COUNT">%2$s</xliff:g> 個)"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"僅允許存取媒體"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"一律允許"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"僅在使用此應用程式時允許"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"允許存取所有相片"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"允許存取所選相片"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"每次都詢問"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"不允許"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"精確位置"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"要允許 &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 存取此裝置上的&lt;b&gt;相片、影片、音樂、音訊和其他檔案&lt;/b&gt;嗎?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"要允許 &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 存取此裝置上的音樂和音訊嗎?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"要允許 &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 存取此裝置上的相片和影片嗎?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取更多相片嗎?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;錄音嗎?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"此應用程式將只能在您使用期間錄音"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;錄音嗎?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"系統會在應用程式存取您複製的文字、圖片或其他內容時顯示訊息"</string>
     <string name="show_password_title" msgid="2877269286984684659">"顯示密碼"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"輸入時短暫顯示字元"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"此應用程式表明可能會與第三方分享<xliff:g id="PERMISSION_NAME">%s</xliff:g>資料"</string>
 </resources>
diff --git a/PermissionController/res/values-zh-rTW-v33/strings.xml b/PermissionController/res/values-zh-rTW-v33/strings.xml
index f0966ae..93013ba 100644
--- a/PermissionController/res/values-zh-rTW-v33/strings.xml
+++ b/PermissionController/res/values-zh-rTW-v33/strings.xml
@@ -27,7 +27,7 @@
     <string name="safety_center_entry_group_with_actions_needed_content_description" msgid="2708884606775932657">"清單。<xliff:g id="ENTRY_TITLE">%1$s</xliff:g>。敬請採取行動。<xliff:g id="ENTRY_SUMMARY">%2$s</xliff:g>"</string>
     <string name="safety_center_entry_group_item_content_description" msgid="7348298582877249787">"清單項目。<xliff:g id="ENTRY_ITEM_TITLE">%1$s</xliff:g>。<xliff:g id="ENTRY_ITEM_SUMMARY">%2$s</xliff:g>"</string>
     <string name="safety_center_entry_content_description" msgid="3639565652938224321">"<xliff:g id="ENTRY_ITEM_TITLE">%1$s</xliff:g>。<xliff:g id="ENTRY_ITEM_SUMMARY">%2$s</xliff:g>"</string>
-    <string name="safety_center_more_issues_card_title" msgid="8599419758014246517">"查看所有快訊"</string>
+    <string name="safety_center_more_issues_card_title" msgid="8599419758014246517">"查看所有警示"</string>
     <string name="safety_center_more_issues_card_expand_action" msgid="7109451851052272946">"{count,plural, =1{展開並查看另外 1 則快訊}other{展開並查看另外 # 則快訊}}"</string>
     <string name="safety_center_issue_card_content_description" msgid="1281390769721765363">"警示。<xliff:g id="ISSUE_CARD_TITLE">%1$s</xliff:g>。<xliff:g id="ISSUE_CARD_SUMMARY">%2$s</xliff:g>"</string>
     <string name="safety_center_issue_card_content_description_with_subtitle" msgid="5504040663935313539">"警示。<xliff:g id="ISSUE_CARD_TITLE">%1$s</xliff:g>。<xliff:g id="ISSUE_CARD_SUBTITLE">%2$s</xliff:g>。<xliff:g id="ISSUE_CARD_SUMMARY">%3$s</xliff:g>"</string>
diff --git a/PermissionController/res/values-zh-rTW/strings.xml b/PermissionController/res/values-zh-rTW/strings.xml
index d831c2c..8dd6639 100644
--- a/PermissionController/res/values-zh-rTW/strings.xml
+++ b/PermissionController/res/values-zh-rTW/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"不要變更「應用程式使用期間」"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"保留「僅允許這一次」"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"更多資訊"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"允許存取所有相片"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"選取相片"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"選取更多相片"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"不要選取更多相片"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"仍不允許"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"關閉"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g>/<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"只允許存取媒體檔案"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"一律允許"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"僅在使用該應用程式時允許"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"允許所有相片"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"允許選取的相片"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"每次都詢問"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"不允許"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"精確位置"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取這部裝置上的&lt;b&gt;相片、影片、音樂、音訊和其他檔案&lt;/b&gt;嗎?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取這部裝置上的音樂和音訊嗎?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取這部裝置上的相片和影片嗎?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取更多相片嗎?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」錄音嗎?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"這個應用程式只有在你使用時才能錄音"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」錄音嗎?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"系統會在應用程式存取你複製的文字、圖片或其他內容時顯示通知訊息"</string>
     <string name="show_password_title" msgid="2877269286984684659">"顯示密碼"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"輸入密碼時,短暫顯示剛輸入的字元"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"這個應用程式表示可能會將「<xliff:g id="PERMISSION_NAME">%s</xliff:g>」資料分享給第三方"</string>
 </resources>
diff --git a/PermissionController/res/values-zu/strings.xml b/PermissionController/res/values-zu/strings.xml
index 2f32981..b5afa9c 100644
--- a/PermissionController/res/values-zu/strings.xml
+++ b/PermissionController/res/values-zu/strings.xml
@@ -32,6 +32,10 @@
     <string name="grant_dialog_button_no_upgrade" msgid="8344732743633736625">"Gcina okuthi “Ngenkathi uhlelo lokusebenza lusebenza”"</string>
     <string name="grant_dialog_button_no_upgrade_one_time" msgid="5125892775684968694">"Gcina “Kulesi sikhathi kuphela”"</string>
     <string name="grant_dialog_button_more_info" msgid="213350268561945193">"Olunye ulwazi"</string>
+    <string name="grant_dialog_button_allow_all_photos" msgid="3688746146785304900">"Vumela Ukufinyelela kuzo zonke izithombe"</string>
+    <string name="grant_dialog_button_allow_selected_photos" msgid="4098620850512492892">"Khetha izithombe"</string>
+    <string name="grant_dialog_button_allow_more_selected_photos" msgid="2003524111894640605">"Khetha izithombe eziningi"</string>
+    <string name="grant_dialog_button_dont_allow_more_selected_photos" msgid="6811842813929146242">"Ungakhethi izithombe eziningi"</string>
     <string name="grant_dialog_button_deny_anyway" msgid="7225905870668915151">"Ungavumeli noma kunjalo"</string>
     <string name="grant_dialog_button_dismiss" msgid="1930399742250226393">"Vula"</string>
     <string name="current_permission_template" msgid="7452035392573329375">"<xliff:g id="CURRENT_PERMISSION_INDEX">%1$s</xliff:g> kokungu-<xliff:g id="PERMISSION_COUNT">%2$s</xliff:g>"</string>
@@ -186,6 +190,8 @@
     <string name="app_permission_button_allow_media_only" msgid="2834282724426046154">"Vumela ukufinyelela kumidiya kuphela"</string>
     <string name="app_permission_button_allow_always" msgid="4573292371734011171">"Vumela sonke isikhathi"</string>
     <string name="app_permission_button_allow_foreground" msgid="1991570451498943207">"Vumela kuphela ngenkathi usebenzisa uhlelo lokusebenza"</string>
+    <string name="app_permission_button_allow_all_photos" msgid="914762549054270764">"Vumela zonke izithombe"</string>
+    <string name="app_permission_button_select_photos" msgid="1022930616634145364">"Vumela izithombe ezikhethiwe"</string>
     <string name="app_permission_button_ask" msgid="3342950658789427">"Buza njalo"</string>
     <string name="app_permission_button_deny" msgid="6016454069832050300">"Ungavumeli"</string>
     <string name="precise_image_description" msgid="6349638632303619872">"Indawo eqondile"</string>
@@ -413,7 +419,7 @@
     <string name="ongoing_usage_dialog_last_separator" msgid="4170995004748832163">" kanye "</string>
     <string name="default_app_search_keyword" msgid="8330125736889689743">"izinhlelo zokusebenza ezizenzakalelayo"</string>
     <string name="permgroup_list_microphone_and_camera" msgid="962768198001487969">"Imakrofoni Nekhamera"</string>
-    <string name="settings_button" msgid="4414988414732479636">"Izilungiselelo"</string>
+    <string name="settings_button" msgid="4414988414732479636">"Amasethingi"</string>
     <string name="default_apps" msgid="5119201969348748639">"Izinhlelo zokusebenza ezizenzakalelayo"</string>
     <string name="no_default_apps" msgid="2593466527182950231">"Azikho izinhlelo zokusebenza ezizenzakalelayo"</string>
     <string name="default_apps_more" msgid="4078194675848858093">"Okuzenzakalelayo okuningi"</string>
@@ -468,6 +474,7 @@
     <string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vumela i-<xliff:g id="APP_NAME">%1$s</xliff:g> ukuba ifinyelele izithombe, amavidiyo, umculo, okulalelwayo, namanye amafayela kule divayisi?"</string>
     <string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vumela i-<xliff:g id="APP_NAME">%1$s</xliff:g> ukuba ifinyelele umculo nokulalelwayo kule divayisi?"</string>
     <string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vumela i-<xliff:g id="APP_NAME">%1$s</xliff:g> ukuba ifinyelele izithombe namavidiyo kule divayisi?"</string>
+    <string name="permgrouprequest_more_photos" msgid="4697813231897226261">"Nikeza i-<xliff:g id="APP_NAME">%1$s</xliff:g> ukufinyelela izithombe ezengeziwe?"</string>
     <string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi irekhode umsindo?"</string>
     <string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Uhlelo lokusebenza luzokwazi ukurekhoda imisindo kuphela kuyilapho usebenzisa uhlelo lokusebenza"</string>
     <string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi irekhode umsindo?"</string>
@@ -578,4 +585,5 @@
     <string name="show_clip_access_notification_summary" msgid="3532020182782112687">"Bonisa umlayezo uma ama-app wakho afinyelela umbhalo, izithombe, noma okunye okuqukethwe okukopishile"</string>
     <string name="show_password_title" msgid="2877269286984684659">"Bonisa amaphasiwedi"</string>
     <string name="show_password_summary" msgid="1110166488865981610">"Bonisa izinhlamvu kancane njengoba uthayipha"</string>
+    <string name="permission_rationale_message_template" msgid="4497650516269082051">"Le-app ithi ingabelana ngedatha ye-<xliff:g id="PERMISSION_NAME">%s</xliff:g> nezinkampani zangaphandle"</string>
 </resources>
diff --git a/PermissionController/res/values/overlayable.xml b/PermissionController/res/values/overlayable.xml
index 6e7973f..6f7d5a8 100644
--- a/PermissionController/res/values/overlayable.xml
+++ b/PermissionController/res/values/overlayable.xml
@@ -61,8 +61,34 @@
             <item type="style" name="PermissionGrantButtonAllowOneTime" />
             <item type="style" name="PermissionGrantButtonDeny" />
             <item type="style" name="PermissionGrantButtonNoUpgrade" />
+
+            <item type="style" name="PermissionGrantPermissionRationaleContent" />
+            <item type="style" name="PermissionGrantPermissionRationaleIcon" />
+            <item type="style" name="PermissionGrantPermissionRationaleMessage" />
+            <item type="style" name="PermissionGrantPermissionRationaleMoreInfoIcon" />
+
             <!-- END PERMISSION GRANT DIALOG -->
 
+            <!-- START PERMISSION RATIONALE DIALOG -->
+
+            <item type="style" name="PermissionRationaleScrollView" />
+            <item type="style" name="PermissionRationaleSingleton" />
+            <item type="style" name="PermissionRationaleDialog" />
+            <item type="style" name="PermissionRationaleContent" />
+
+            <item type="style" name="PermissionRationaleTitleContainer" />
+            <item type="style" name="PermissionRationaleTitleIcon" />
+            <item type="style" name="PermissionRationaleTitleMessage" />
+
+            <item type="style" name="PermissionRationaleSectionOuterContainer" />
+            <item type="style" name="PermissionRationaleSectionIcon" />
+            <item type="style" name="PermissionRationaleSectionInnerContainer" />
+            <item type="style" name="PermissionRationaleSectionTitle" />
+            <item type="style" name="PermissionRationaleSectionMessage" />
+
+            <item type="style" name="PermissionRationaleButtonContainer" />
+
+            <!-- END PERMISSION RATIONALE DIALOG -->
 
             <!-- START PERMISSION REVIEW SCREEN -->
             <item type="style" name="PermissionReview" />
diff --git a/PermissionController/res/values/strings.xml b/PermissionController/res/values/strings.xml
index 79ba3d3..a660341 100644
--- a/PermissionController/res/values/strings.xml
+++ b/PermissionController/res/values/strings.xml
@@ -67,6 +67,18 @@
     <!-- Title for the dialog button to get more info about a permission. [CHAR LIMIT=15] -->
     <string name="grant_dialog_button_more_info">More info</string>
 
+    <!-- Title for the dialog button to allow access to all photos. [CHAR LIMIT=60] -->
+    <string name="grant_dialog_button_allow_all_photos">Allow access to all photos</string>
+
+    <!-- Title for the dialog button to allow access to select photos to be shared. [CHAR LIMIT=60] -->
+    <string name="grant_dialog_button_allow_selected_photos">Select photos</string>
+
+    <!-- Title for the dialog button to allow access to select more photos to be shared. [CHAR LIMIT=60] -->
+    <string name="grant_dialog_button_allow_more_selected_photos">Select more photos</string>
+
+    <!-- Title for the dialog button to not allow access to select more photos to be shared. [CHAR LIMIT=60] -->
+    <string name="grant_dialog_button_dont_allow_more_selected_photos">Don\u2019t select more photos</string>
+
     <!-- Title for the dialog button to deny a permission grant despite a warning of implications. [CHAR LIMIT=30] -->
     <string name="grant_dialog_button_deny_anyway">Don\u2019t allow anyway</string>
 
@@ -593,6 +605,12 @@
     <!-- Title for the dialog button to allow a permission grant only when the app is in the foreground. [CHAR LIMIT=60] -->
     <string name="app_permission_button_allow_foreground">Allow only while using the app</string>
 
+    <!-- Title for the dialog button to allow the user to allow all photos. [CHAR LIMIT=60] -->
+    <string name="app_permission_button_allow_all_photos">Allow all photos</string>
+
+    <!-- Title for the dialog button to allow the user to select photos. [CHAR LIMIT=60] -->
+    <string name="app_permission_button_select_photos">Allow selected photos</string>
+
     <!-- Title for the dialog button to require an app to ask for a permission next time they need it. [CHAR LIMIT=60] -->
     <string name="app_permission_button_ask">Ask every time</string>
 
@@ -1191,9 +1209,17 @@
     <!-- Description for the watch profile role. [CHAR LIMIT=NONE] -->
     <string name="role_watch_description"><xliff:g id="app_name" example="Wear">%1$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts and Calendar permissions.</string>
 
+    <!-- TODO(b/261147998): STOPSHIP update with finalized description string -->
+    <!-- Description for the glasses profile role. [CHAR LIMIT=NONE] -->
+    <string name="role_companion_device_glasses_description"><xliff:g id="app_name" example="GlassesApp">%1$s</xliff:g> will be allowed to access your Phone, SMS, Contacts, Microphone and Nearby devices permissions.</string>
+
     <!-- Description for the app streaming profile role. [CHAR LIMIT=NONE] -->
     <string name="role_app_streaming_description"><xliff:g id="app_name" example="Cross-Device Communciation">%1$s</xliff:g> will be allowed to interact with your notifications and stream your apps to the connected device.</string>
 
+    <!-- TODO(b/261147998): STOPSHIP update with finalized description string -->
+    <!-- Description for the nearby device streaming profile role. [CHAR LIMIT=NONE] -->
+    <string name="role_companion_device_nearby_device_streaming_description"><xliff:g id="app_name" example="NearbyStreamer">%1$s</xliff:g> will be allowed to stream content to nearby devices.</string>
+
     <!-- Description for the companion device computer profile role. [CHAR LIMIT=NONE] -->
     <string name="role_companion_device_computer_description">This service shares your photos, media, and notifications from your phone to other devices.</string>
 
@@ -1434,6 +1460,10 @@
     <string name="permgrouprequest_read_media_visual">Allow &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access photos and videos on this device?</string>
 
     <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
+    <string name="permgrouprequest_more_photos">Grant
+        &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> access to more photos?</string>
+
+    <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
     <string name="permgrouprequest_microphone">Allow
         &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to record audio?</string>
     <!-- Subtitle of the message shown to the user when the apps requests permission to use the microphone only while app is in foreground [CHAR LIMIT=150]-->
@@ -1691,4 +1721,13 @@
     <!-- Summary for toggle controlling whether to show the first letter while typing passwords. [CHAR LIMIT=NONE] -->
     <string name="show_password_summary">Display characters briefly as you type</string>
 
+    <!-- TODO(b/259279178): update with finalized permission rationale strings -->
+    <!-- Template for the permission rationale message when an app requests a permission. Third
+    parties are other organizations outside of the app developer. These could be companies or even
+    governmental organizations. But because we aren't able to be inclusive of all possibilities,
+    phrasing should be as generic as possible while still helping users understand they aren't just
+    sharing data with the developer company. [CHAR LIMIT=100] -->
+    <string name="permission_rationale_message_template">This app stated it may share
+        <xliff:g id="permission_name" example="location">%s</xliff:g> data with third parties</string>
+
 </resources>
diff --git a/PermissionController/res/values/styles.xml b/PermissionController/res/values/styles.xml
index bb1c7fd..041747a 100644
--- a/PermissionController/res/values/styles.xml
+++ b/PermissionController/res/values/styles.xml
@@ -104,6 +104,14 @@
         <item name="android:orientation">horizontal</item>
     </style>
 
+    <style name="PermissionLocationAccuracyRadioGroupMaterial3">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginBottom">24dp</item>
+        <item name="android:gravity">center_horizontal</item>
+        <item name="android:orientation">horizontal</item>
+    </style>
+
     <style name="PermissionLocationAccuracyRadioFine">
         <item name="android:button">@null</item>
         <item name="android:background">@null</item>
@@ -127,6 +135,12 @@
         <item name="android:layout_marginBottom">24dp</item>
     </style>
 
+    <style name="PermissionLocationAccuracyFineImageViewMaterial3">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginBottom">24dp</item>
+    </style>
+
     <style name="PermissionLocationAccuracyCoarseImageView">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
@@ -134,6 +148,12 @@
         <item name="android:layout_marginBottom">24dp</item>
     </style>
 
+    <style name="PermissionLocationAccuracyCoarseImageViewMaterial3">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginBottom">24dp</item>
+    </style>
+
     <style name="PermissionGrantButtonList">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
@@ -163,6 +183,44 @@
         <item name="android:background">?android:attr/selectableItemBackground</item>
     </style>
 
+    <style name="PermissionGrantPermissionRationaleContent">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginStart">24dp</item>
+        <item name="android:layout_marginEnd">24dp</item>
+        <item name="android:layout_marginBottom">24dp</item>
+        <item name="android:padding">12dp</item>
+        <item name="android:orientation">horizontal</item>
+        <item name="android:background">@drawable/grant_dialog_permission_rationale_background</item>
+    </style>
+
+    <style name="PermissionGrantPermissionRationaleIcon">
+        <item name="android:layout_width">20dp</item>
+        <item name="android:layout_height">20dp</item>
+        <item name="android:layout_gravity">start|center_vertical</item>
+        <item name="android:scaleType">centerInside</item>
+        <item name="android:tint">?android:attr/textColorSecondary</item>
+    </style>
+
+    <style name="PermissionGrantPermissionRationaleMessage"
+           parent="@android:style/TextAppearance.DeviceDefault">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:layout_width">0dp</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_weight">1</item>
+        <item name="android:layout_marginStart">12dp</item>
+        <item name="android:layout_marginEnd">12dp</item>
+        <item name="android:textSize">14sp</item>
+    </style>
+
+    <style name="PermissionGrantPermissionRationaleMoreInfoIcon">
+        <item name="android:layout_width">20dp</item>
+        <item name="android:layout_height">20dp</item>
+        <item name="android:layout_gravity">end|center_vertical</item>
+        <item name="android:scaleType">centerInside</item>
+        <item name="android:tint">?android:attr/textColorSecondary</item>
+    </style>
+
     <!-- for use in overlays -->
     <style name="PermissionGrantButtonAllow"
            parent="@style/PermissionGrantButton"></style>
@@ -170,6 +228,14 @@
            parent="@style/PermissionGrantButton"></style>
     <style name="PermissionGrantButtonAllowOneTime"
            parent="@style/PermissionGrantButton"></style>
+    <style name="PermissionGrantButtonAllowAllPhotos"
+           parent="@style/PermissionGrantButton"></style>
+    <style name="PermissionGrantButtonAllowMorePhotos"
+           parent="@style/PermissionGrantButton"></style>
+    <style name="PermissionGrantButtonAllowSelectedPhotos"
+           parent="@style/PermissionGrantButton"></style>
+    <style name="PermissionGrantButtonDontAllowMorePhotos"
+           parent="@style/PermissionGrantButton"></style>
     <style name="PermissionGrantButtonDeny"
            parent="@style/PermissionGrantButton"></style>
     <style name="PermissionGrantButtonNoUpgrade"
@@ -185,9 +251,124 @@
            parent="@style/PermissionGrantButton"></style>
     <style name="PermissionGrantButtonNoUpgradeMaterial3"
            parent="@style/PermissionGrantButton"></style>
+    <style name="PermissionGrantButtonAllowAllPhotosMaterial3"
+           parent="@style/PermissionGrantButton"></style>
+    <style name="PermissionGrantButtonAllowSelectedPhotosMaterial3"
+           parent="@style/PermissionGrantButton"></style>
+    <style name="PermissionGrantButtonAllowMorePhotosMaterial3"
+           parent="@style/PermissionGrantButton"></style>
+    <style name="PermissionGrantButtonDontAllowMorePhotosMaterial3"
+           parent="@style/PermissionGrantButton"></style>
 
     <!-- END PERMISSION GRANT DIALOG -->
 
+    <!-- START PERMISSION RATIONALE DIALOG -->
+
+    <style name="PermissionRationaleScrollView">
+        <item name="android:scrollbars">none</item>
+        <item name="android:fillViewport">true</item>
+        <item name="android:clipChildren">false</item>
+    </style>
+
+    <style name="PermissionRationaleSingleton">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:gravity">center</item>
+    </style>
+
+    <style name="PermissionRationaleDialog">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:background">?android:attr/windowBackground</item>
+        <item name="android:orientation">vertical</item>
+        <item name="android:showDividers">middle</item>
+    </style>
+
+    <style name="PermissionRationaleContent">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:orientation">vertical</item>
+        <item name="android:paddingTop">24dp</item>
+        <item name="android:paddingBottom">18dp</item>
+        <item name="android:paddingStart">24dp</item>
+        <item name="android:paddingEnd">24dp</item>
+    </style>
+
+    <style name="PermissionRationaleTitleContainer">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:orientation">vertical</item>
+        <item name="android:gravity">center</item>
+    </style>
+
+    <style name="PermissionRationaleTitleIcon">
+        <item name="android:layout_width">32dp</item>
+        <item name="android:layout_height">32dp</item>
+        <item name="android:layout_marginBottom">16dp</item>
+        <item name="android:tint">?android:attr/textColorSecondary</item>
+        <item name="android:scaleType">centerInside</item>
+    </style>
+
+    <style name="PermissionRationaleTitleMessage"
+           parent="@android:style/TextAppearance.DeviceDefault.WindowTitle">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:gravity">center</item>
+    </style>
+
+    <style name="PermissionRationaleSectionOuterContainer">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:orientation">horizontal</item>
+        <item name="android:layout_marginTop">16dp</item>
+    </style>
+
+    <style name="PermissionRationaleSectionIcon">
+        <item name="android:layout_width">20dp</item>
+        <item name="android:layout_height">20dp</item>
+        <item name="android:tint">?android:attr/textColorSecondary</item>
+        <item name="android:scaleType">centerInside</item>
+    </style>
+
+    <style name="PermissionRationaleSectionInnerContainer">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:orientation">vertical</item>
+        <item name="android:layout_marginStart">16dp</item>
+    </style>
+
+    <style name="PermissionRationaleSectionTitle"
+           parent="@android:style/TextAppearance.DeviceDefault.Medium">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:lineHeight">20sp</item>
+        <item name="android:lineSpacingMultiplier">1.25</item>
+    </style>
+
+    <style name="PermissionRationaleSectionMessage"
+           parent="@android:style/TextAppearance.DeviceDefault">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginTop">4dp</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:lineHeight">20sp</item>
+        <item name="android:lineSpacingMultiplier">1.25</item>
+    </style>
+
+    <style name="PermissionRationaleButtonContainer">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginTop">32dp</item>
+        <item name="android:orientation">horizontal</item>
+        <item name="android:gravity">end</item>
+    </style>
+
+    <!-- END PERMISSION RATIONALE DIALOG -->
+
     <!-- START PERMISSION REVIEW SCREEN -->
 
     <style name="PermissionReview">
diff --git a/PermissionController/res/values/themes.xml b/PermissionController/res/values/themes.xml
index 38b8347..76196a0 100644
--- a/PermissionController/res/values/themes.xml
+++ b/PermissionController/res/values/themes.xml
@@ -105,7 +105,7 @@
         <item name="android:background">@color/divider_color_primary</item>
     </style>
 
-    <style name="Theme.DeviceDefault.Dialog.Alert.DayNight" parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
+    <style name="Theme.DeviceDefault.Dialog.NoActionBar.DayNight" parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar" />
 
 
 <!-- Do not allow OEMs to overlay these themes.
diff --git a/PermissionController/res/xml-v34/safety_center_subpage.xml b/PermissionController/res/xml-v34/safety_center_subpage.xml
new file mode 100644
index 0000000..0ffb34e
--- /dev/null
+++ b/PermissionController/res/xml-v34/safety_center_subpage.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <com.android.permissioncontroller.safetycenter.ui.ComparablePreferenceCategory
+        android:key="subpage_entry_group"
+        app:selectable="false" />
+</PreferenceScreen>
diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml
index 4c5f7da..be42c08 100644
--- a/PermissionController/res/xml/roles.xml
+++ b/PermissionController/res/xml/roles.xml
@@ -84,6 +84,7 @@
         <permission name="android.permission.BLUETOOTH_ADVERTISE" minSdkVersion="31" />
         <permission name="android.permission.BLUETOOTH_CONNECT" minSdkVersion="31" />
         <permission name="android.permission.BLUETOOTH_SCAN" minSdkVersion="31" />
+        <permission name="android.permission.NEARBY_WIFI_DEVICES" minSdkVersion="33" />
     </permission-set>
 
     <permission-set name="notifications">
@@ -101,7 +102,8 @@
         label="@string/role_assistant_label"
         overrideUserWhenGranting="true"
         requestable="false"
-        shortLabel="@string/role_assistant_short_label">
+        shortLabel="@string/role_assistant_short_label"
+        uiBehavior="AssistantRoleUiBehavior">
         <required-components>
             <!-- Qualified components are determined int AssistantRoleBehavior. This comment here is
                  ignored and represents just a rough description
@@ -159,7 +161,8 @@
         overrideUserWhenGranting="true"
         requestDescription="@string/role_browser_request_description"
         requestTitle="@string/role_browser_request_title"
-        shortLabel="@string/role_browser_short_label">
+        shortLabel="@string/role_browser_short_label"
+        uiBehavior="BrowserRoleUiBehavior">
         <!--
           ~ Required components matching is handled in BrowserRoleBehavior because it needs the
           ~ PackageManager.MATCH_ALL flag and other manual filtering, which cannot fit in our
@@ -201,7 +204,8 @@
         requestDescription="@string/role_dialer_request_description"
         requestTitle="@string/role_dialer_request_title"
         searchKeywords="@string/role_dialer_search_keywords"
-        shortLabel="@string/role_dialer_short_label">
+        shortLabel="@string/role_dialer_short_label"
+        uiBehavior="DialerRoleUiBehavior">
         <required-components>
             <activity>
                 <intent-filter>
@@ -287,7 +291,8 @@
         requestDescription="@string/role_sms_request_description"
         requestTitle="@string/role_sms_request_title"
         searchKeywords="@string/role_sms_search_keywords"
-        shortLabel="@string/role_sms_short_label">
+        shortLabel="@string/role_sms_short_label"
+        uiBehavior="SmsRoleUiBehavior">
         <required-components>
             <receiver permission="android.permission.BROADCAST_SMS">
                 <intent-filter>
@@ -377,7 +382,8 @@
         requestTitle="@string/role_emergency_request_title"
         searchKeywords="@string/role_emergency_search_keywords"
         shortLabel="@string/role_emergency_short_label"
-        systemOnly="true">
+        systemOnly="true"
+        uiBehavior="EmergencyRoleUiBehavior">
         <required-components>
             <activity>
                 <intent-filter>
@@ -407,7 +413,8 @@
         requestDescription="@string/role_home_request_description"
         requestTitle="@string/role_home_request_title"
         searchKeywords="@string/role_home_search_keywords"
-        shortLabel="@string/role_home_short_label">
+        shortLabel="@string/role_home_short_label"
+        uiBehavior="HomeRoleUiBehavior">
         <!-- Also used by HomeRoleBehavior.getFallbackHolder(). -->
         <required-components>
             <activity>
@@ -600,6 +607,7 @@
             <permission name="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY" minSdkVersion="33" />
             <permission name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"
                 minSdkVersion="33" />
+            <permission name="android.permission.MANAGE_DEVICE_LOCK_STATE" minSdkVersion="34" />
         </permissions>
     </role>
 
@@ -1108,6 +1116,38 @@
        </permissions>
     </role>
 
+    <role
+        name="android.app.role.COMPANION_DEVICE_GLASSES"
+        description="@string/role_companion_device_glasses_description"
+        exclusive="false"
+        minSdkVersion="34"
+        systemOnly="false"
+        visible="false">
+        <permissions>
+            <permission-set name="contacts" />
+            <permission-set name="microphone" />
+            <permission-set name="nearby_devices" />
+            <permission-set name="phone" />
+            <permission-set name="sms" />
+       </permissions>
+    </role>
+
+    <role
+        name="android.app.role.COMPANION_DEVICE_NEARBY_DEVICE_STREAMING"
+        allowBypassingQualification="true"
+        description="@string/role_companion_device_nearby_device_streaming_description"
+        exclusive="false"
+        minSdkVersion="34"
+        systemOnly="true"
+        visible="false">
+        <permissions>
+            <permission-set name="nearby_devices" />
+            <permission name="android.permission.CREATE_VIRTUAL_DEVICE" />
+            <permission name="android.permission.ADD_TRUSTED_DISPLAY" />
+            <permission name="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY" />
+        </permissions>
+    </role>
+
      <role
         name="android.app.role.SYSTEM_SUPERVISION"
         defaultHolders="config_systemSupervision"
@@ -1342,4 +1382,17 @@
             <permission name="android.permission.NET_TUNNELING" />
         </permissions>
     </role>
+
+    <!--
+     ~ A role assigned to the financing kiosk app
+    -->
+    <role
+        name="android.app.role.FINANCED_DEVICE_KIOSK"
+        exclusive="true"
+        minSdkVersion="34"
+        visible="false">
+        <permissions>
+            <permission name="android.permission.MANAGE_DEVICE_LOCK_STATE" />
+        </permissions>
+    </role>
 </roles>
diff --git a/PermissionController/src/com/android/permissioncontroller/Constants.java b/PermissionController/src/com/android/permissioncontroller/Constants.java
index ae399f0..2d79549 100644
--- a/PermissionController/src/com/android/permissioncontroller/Constants.java
+++ b/PermissionController/src/com/android/permissioncontroller/Constants.java
@@ -70,11 +70,11 @@
      */
     public static final int PERIODIC_ACCESSIBILITY_CHECK_JOB_ID = 6;
 
-    /**
-     * ID for Safety Centers delayed job scheduled after boot and after Safety Center is enabled
-     *
-     * @see
+     /**
+     * ID for Safety Centers periodic background refresh job, scheduled after boot and after Safety
+     * Center is enabled, in {@link
      * com.android.permissioncontroller.safetycenter.service.SafetyCenterBackgroundRefreshJobService
+     * }.
      */
     public static final int SAFETY_CENTER_BACKGROUND_REFRESH_JOB_ID = 7;
 
diff --git a/PermissionController/src/com/android/permissioncontroller/PermissionControllerApplication.java b/PermissionController/src/com/android/permissioncontroller/PermissionControllerApplication.java
index bc7a1d0..dad0dc3 100644
--- a/PermissionController/src/com/android/permissioncontroller/PermissionControllerApplication.java
+++ b/PermissionController/src/com/android/permissioncontroller/PermissionControllerApplication.java
@@ -27,11 +27,14 @@
 import androidx.annotation.RequiresApi;
 
 import com.android.modules.utils.build.SdkLevel;
+import com.android.permissioncontroller.permission.utils.KotlinUtils;
 import com.android.permissioncontroller.permission.utils.Utils;
 import com.android.permissioncontroller.privacysources.SafetyCenterAccessibilityListener;
-import com.android.permissioncontroller.role.model.Role;
-import com.android.permissioncontroller.role.model.Roles;
 import com.android.permissioncontroller.role.ui.SpecialAppAccessListActivity;
+import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.RoleParserInitializer;
+import com.android.role.controller.model.Roles;
 
 public final class PermissionControllerApplication extends Application {
 
@@ -44,10 +47,14 @@
         sInstance = this;
 
         PackageItemInfo.forceSafeLabels();
+        RoleParserInitializer.initialize();
         updateSpecialAppAccessListActivityEnabledState();
         if (SdkLevel.isAtLeastT()) {
             addAccessibilityListener();
         }
+        if (Utils.isHealthPermissionUiEnabled()) {
+            KotlinUtils.INSTANCE.addHealthPermissions(this);
+        }
     }
 
     /**
@@ -64,7 +71,7 @@
         for (int i = 0; i < rolesSize; i++) {
             Role role = roles.valueAt(i);
 
-            if (!role.isAvailable(this) || !role.isVisible(this)) {
+            if (!role.isAvailable(this) || !RoleUiBehaviorUtils.isVisible(role, this)) {
                 continue;
             }
             if (!role.isExclusive()) {
diff --git a/PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING
index 010513f..16ee9b4 100644
--- a/PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING
+++ b/PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING
@@ -1,10 +1,10 @@
 {
     "presubmit": [
         {
-            "name": "CtsOsTestCases",
+            "name": "CtsHibernationTestCases",
             "options": [
                 {
-                    "include-filter": "android.os.cts.AppHibernationIntegrationTest"
+                    "include-filter": "android.hibernation.cts.AppHibernationIntegrationTest"
                 }
             ]
         }
diff --git a/PermissionController/src/com/android/permissioncontroller/incident/ReportDetails.java b/PermissionController/src/com/android/permissioncontroller/incident/ReportDetails.java
index 95fd251..d19ef46 100644
--- a/PermissionController/src/com/android/permissioncontroller/incident/ReportDetails.java
+++ b/PermissionController/src/com/android/permissioncontroller/incident/ReportDetails.java
@@ -100,7 +100,7 @@
             final IncidentHeaderProto header = incident.getHeader(i);
             if (header.hasReason()) {
                 final String reason = header.getReason();
-                if (reason != null && reason.length() > 0) {
+                if (reason.length() > 0) {
                     result.add(reason);
                 }
             }
@@ -122,9 +122,6 @@
             final int setsCount = section.getSetsCount();
             for (int i = 0; i < setsCount; i++) {
                 final RestrictedImageSetProto set = section.getSets(i);
-                if (set == null) {
-                    continue;
-                }
                 final int imageCount = set.getImagesCount();
                 for (int j = 0; j < imageCount; j++) {
                     // Hard cap on number of images, as a guardrail.
@@ -135,18 +132,12 @@
                     }
 
                     final RestrictedImageProto image = set.getImages(j);
-                    if (image == null) {
-                        continue;
-                    }
                     final String mimeType = image.getMimeType();
                     if (!("image/jpeg".equals(mimeType)
                             || "image/png".equals(mimeType))) {
                         throw new ParseException("Unsupported image type " + mimeType);
                     }
                     final ByteString bytes = image.getImageData();
-                    if (bytes == null) {
-                        continue;
-                    }
                     final byte[] buf = bytes.toByteArray();
                     if (buf.length == 0) {
                         continue;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING
index de7511e..b65eb67 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING
+++ b/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING
@@ -27,15 +27,9 @@
             ]
         },
         {
-            "name": "CtsOsTestCases",
+            "name": "CtsHibernationTestCases",
             "options": [
                 {
-                    "include-filter": "android.os.cts.AutoRevokeTest"
-                },
-                {
-                    "include-filter": "android.os.cts.AppHibernationIntegrationTest"
-                },
-                {
                     "exclude-annotation": "android.platform.test.annotations.FlakyTest"
                 }
             ]
@@ -69,17 +63,11 @@
             ]
         },
         {
-            "name": "CtsOsTestCases[com.google.android.permission.apex]",
+            "name": "CtsHibernationTestCases[com.google.android.permission.apex]",
             "options": [
-                {
-                    "include-filter": "android.os.cts.AutoRevokeTest"
-                },
-                {
-                    "include-filter": "android.os.cts.AppHibernationIntegrationTest"
-                },
                 // TODO(b/238677038): This test currently fails on R base image
                 {
-                    "exclude-filter": "android.os.cts.AutoRevokeTest#testUnusedApp_uninstallApp"
+                    "exclude-filter": "android.hibernation.cts.AutoRevokeTest#testUnusedApp_uninstallApp"
                 },
                 {
                     "exclude-annotation": "android.platform.test.annotations.FlakyTest"
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/LightInstallSourceInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/LightInstallSourceInfoLiveData.kt
new file mode 100644
index 0000000..543d8ea
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/LightInstallSourceInfoLiveData.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.permission.data
+
+import android.app.Application
+import android.content.Context
+import android.content.pm.InstallSourceInfo
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.util.Log
+import com.android.permissioncontroller.PermissionControllerApplication
+import com.android.permissioncontroller.permission.model.livedatatypes.LightInstallSourceInfo
+import com.android.permissioncontroller.permission.model.livedatatypes.LightInstallSourceInfo.Companion.UNKNOWN_INSTALL_SOURCE
+import com.android.permissioncontroller.permission.utils.Utils
+import kotlinx.coroutines.Job
+
+/**
+ * [LightInstallSourceInfo] [LiveData] for the specified package
+ *
+ * @param app current Application
+ * @param packageName name of the package to get InstallSourceInfo for
+ * @param user The user of the package
+ */
+class LightInstallSourceInfoLiveData
+private constructor(
+    private val app: Application,
+    private val packageName: String,
+    private val user: UserHandle
+) : SmartAsyncMediatorLiveData<LightInstallSourceInfo>(),
+    PackageBroadcastReceiver.PackageBroadcastListener {
+
+    override fun onActive() {
+        super.onActive()
+        PackageBroadcastReceiver.addChangeCallback(packageName, this)
+    }
+
+    override fun onInactive() {
+        super.onInactive()
+        PackageBroadcastReceiver.removeChangeCallback(packageName, this)
+    }
+
+    /**
+     * Callback from the PackageBroadcastReceiver
+     *
+     * @param packageName the name of the package which was updated.
+     */
+    override fun onPackageUpdate(packageName: String) {
+        update()
+    }
+
+    override suspend fun loadDataAndPostValue(job: Job) {
+        if (job.isCancelled) {
+            return
+        }
+
+        val lightInstallSourceInfo: LightInstallSourceInfo =
+            try {
+                val userContext = Utils.getUserContext(app, user)
+                LightInstallSourceInfo(
+                    getInstallSourceInfo(userContext, packageName).installingPackageName)
+            } catch (e: PackageManager.NameNotFoundException) {
+                Log.w(LOG_TAG, "InstallSourceInfo for $packageName not found")
+                SafetyLabelInfoLiveData.invalidateSingle(packageName to user)
+                UNKNOWN_INSTALL_SOURCE
+            }
+        postValue(lightInstallSourceInfo)
+    }
+
+    companion object :
+        DataRepositoryForPackage<Pair<String, UserHandle>, LightInstallSourceInfoLiveData>() {
+        private val LOG_TAG = LightInstallSourceInfoLiveData::class.java.simpleName
+
+        override fun newValue(key: Pair<String, UserHandle>): LightInstallSourceInfoLiveData {
+            return LightInstallSourceInfoLiveData(
+                PermissionControllerApplication.get(), key.first, key.second)
+        }
+
+        /** Returns the [InstallSourceInfo] for the given package */
+        @Throws(PackageManager.NameNotFoundException::class)
+        private fun getInstallSourceInfo(context: Context, packageName: String): InstallSourceInfo {
+            return context.packageManager.getInstallSourceInfo(packageName)
+        }
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/PackageBroadcastReceiver.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/PackageBroadcastReceiver.kt
index 9dc16e3..c66e7a7 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/PackageBroadcastReceiver.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PackageBroadcastReceiver.kt
@@ -161,6 +161,8 @@
             HibernationSettingStateLiveData.invalidateAllForPackage(packageName)
             LightAppPermGroupLiveData.invalidateAllForPackage(packageName)
             AppPermGroupUiInfoLiveData.invalidateAllForPackage(packageName)
+            SafetyLabelInfoLiveData.invalidateAllForPackage(packageName)
+            LightInstallSourceInfoLiveData.invalidateAllForPackage(packageName)
         }
     }
 
@@ -176,4 +178,4 @@
          */
         fun onPackageUpdate(packageName: String)
     }
-}
\ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/SafetyLabelInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/SafetyLabelInfoLiveData.kt
new file mode 100644
index 0000000..64bd21e
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/SafetyLabelInfoLiveData.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.permission.data
+
+import android.app.Application
+import android.content.pm.PackageManager
+import android.os.PersistableBundle
+import android.os.UserHandle
+import android.util.Log
+import com.android.permission.safetylabel.DataCategoryConstants
+import com.android.permission.safetylabel.DataLabelConstants
+import com.android.permission.safetylabel.DataTypeConstants
+import com.android.permission.safetylabel.SafetyLabel
+import com.android.permissioncontroller.PermissionControllerApplication
+import com.android.permissioncontroller.permission.model.livedatatypes.SafetyLabelInfo
+import com.android.permissioncontroller.permission.utils.KotlinUtils.isPlaceholderSafetyLabelDataEnabled
+import kotlinx.coroutines.Job
+
+/**
+ * [SafetyLabelInfo] [LiveData] for the specified package
+ *
+ * @param app current Application
+ * @param packageName name of the package to get SafetyLabel information for
+ * @param user The user of the package
+ */
+class SafetyLabelInfoLiveData
+private constructor(
+    private val app: Application,
+    private val packageName: String,
+    private val user: UserHandle
+) :
+    SmartAsyncMediatorLiveData<SafetyLabelInfo>(),
+    PackageBroadcastReceiver.PackageBroadcastListener {
+
+    private val lightInstallSourceInfoLiveData = LightInstallSourceInfoLiveData[packageName, user]
+
+    init {
+        addSource(lightInstallSourceInfoLiveData) { update() }
+
+        update()
+    }
+
+    override fun onActive() {
+        super.onActive()
+        PackageBroadcastReceiver.addChangeCallback(packageName, this)
+    }
+
+    override fun onInactive() {
+        super.onInactive()
+        PackageBroadcastReceiver.removeChangeCallback(packageName, this)
+    }
+
+    /**
+     * Callback from the PackageBroadcastReceiver
+     *
+     * @param packageName the name of the package which was updated.
+     */
+    override fun onPackageUpdate(packageName: String) {
+        update()
+    }
+
+    override suspend fun loadDataAndPostValue(job: Job) {
+        if (job.isCancelled) {
+            return
+        }
+
+        if (lightInstallSourceInfoLiveData.isStale) {
+            return
+        }
+
+        // TODO(b/261607291): Add support preinstall apps that provide SafetyLabel. Installing
+        //  package is null until updated from an app store
+        val installSourcePackageName = lightInstallSourceInfoLiveData.value?.installingPackageName
+        if (installSourcePackageName == null) {
+            postValue(SafetyLabelInfo.UNAVAILABLE)
+            return
+        }
+
+        val safetyLabelInfo: SafetyLabelInfo =
+            try {
+                val metadataBundle: PersistableBundle? = getAppMetadata()
+                SafetyLabelInfo(
+                    SafetyLabel.getSafetyLabelFromMetadata(metadataBundle),
+                    installSourcePackageName)
+            } catch (e: PackageManager.NameNotFoundException) {
+                Log.w(LOG_TAG, "SafetyLabel for $packageName not found")
+                invalidateSingle(packageName to user)
+                SafetyLabelInfo.UNAVAILABLE
+            }
+        postValue(safetyLabelInfo)
+    }
+
+    // TODO(b/257293222): Update when hooking up PackageManager APIs
+    private fun getAppMetadata(): PersistableBundle? {
+        return if (isPlaceholderSafetyLabelDataEnabled()) {
+            placeholderMetadataBundle()
+        } else {
+            null
+        }
+    }
+
+    // TODO(b/257293222): Remove when hooking up PackageManager APIs
+    private fun placeholderMetadataBundle(): PersistableBundle {
+        val approximateLocationBundle = PersistableBundle().apply {
+            putIntArray(
+                "purposes",
+                (1..7).toList().toIntArray())
+        }
+
+        val locationBundle = PersistableBundle().apply {
+            putPersistableBundle(
+                DataTypeConstants.LOCATION_APPROX_LOCATION,
+                approximateLocationBundle)
+        }
+
+        val dataSharedBundle = PersistableBundle().apply {
+            putPersistableBundle(DataCategoryConstants.CATEGORY_LOCATION, locationBundle)
+        }
+
+        val dataLabelBundle = PersistableBundle().apply {
+            putPersistableBundle(DataLabelConstants.DATA_USAGE_SHARED, dataSharedBundle)
+        }
+
+        val safetyLabelBundle = PersistableBundle().apply {
+            putPersistableBundle("data_labels", dataLabelBundle)
+        }
+
+        return PersistableBundle().apply {
+            putPersistableBundle("safety_labels", safetyLabelBundle)
+        }
+    }
+
+    companion object : DataRepositoryForPackage<Pair<String, UserHandle>, SafetyLabelInfoLiveData>(
+    ) {
+        private val LOG_TAG = SafetyLabelInfoLiveData::class.java.simpleName
+
+        override fun newValue(key: Pair<String, UserHandle>): SafetyLabelInfoLiveData {
+            return SafetyLabelInfoLiveData(PermissionControllerApplication.get(), key.first,
+                key.second)
+        }
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt
new file mode 100644
index 0000000..d0b44f6
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.permission.data.v31
+
+import android.app.AppOpsManager
+import android.app.Application
+import android.os.UserHandle
+import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData
+import com.android.permissioncontroller.permission.data.StandardPermGroupNamesLiveData
+import com.android.permissioncontroller.permission.model.livedatatypes.v31.LightPackageOps
+import com.android.permissioncontroller.permission.utils.PermissionMapping
+import kotlinx.coroutines.Job
+
+/**
+ * LiveData class tracking [LightPackageOps] for all packages on the device and for all system
+ * permission groups' ops.
+ *
+ * App ops data is retrieved from [AppOpsManager] and is updated whenever app ops data changes are
+ * heard.
+ */
+class AllLightPackageOpsLiveData(app: Application) :
+    SmartAsyncMediatorLiveData<Map<Pair<String, UserHandle>, LightPackageOps>>(),
+    AppOpsManager.OnOpActiveChangedListener,
+    AppOpsManager.OnOpNotedListener,
+    AppOpsManager.OnOpChangedListener {
+
+    private val appOpsManager = app.getSystemService(AppOpsManager::class.java)!!
+    private var opNames: Set<String> = getOpNames(StandardPermGroupNamesLiveData.value)
+
+    init {
+        addSource(StandardPermGroupNamesLiveData) {
+            opNames = getOpNames(it)
+            update()
+        }
+    }
+
+    override fun onActive() {
+        super.onActive()
+
+        try {
+            appOpsManager.startWatchingActive(opNames.toTypedArray(), { it.run() }, this)
+        } catch (ignored: IllegalArgumentException) {
+            // Older builds may not support all requested app ops.
+        }
+
+        opNames.forEach {
+            try {
+                appOpsManager.startWatchingMode(it, /* all packages */ null, this)
+            } catch (ignored: IllegalArgumentException) {
+                // Older builds may not support all requested app ops.
+            }
+        }
+    }
+
+    override fun onInactive() {
+        super.onInactive()
+
+        appOpsManager.stopWatchingActive(this)
+        appOpsManager.stopWatchingMode(this)
+    }
+
+    override suspend fun loadDataAndPostValue(job: Job) {
+        val packageOpsList =
+            try {
+                appOpsManager.getPackagesForOps(opNames.toTypedArray())
+            } catch (e: NullPointerException) {
+                // Older builds may not support all requested app ops.
+                emptyList<AppOpsManager.PackageOps>()
+            }
+
+        postValue(
+            packageOpsList.associateBy(
+                { Pair(it.packageName, UserHandle.getUserHandleForUid(it.uid)) },
+                { LightPackageOps(opNames, it) }))
+    }
+
+    override fun onOpChanged(op: String?, packageName: String?) {
+        update()
+    }
+
+    override fun onOpActiveChanged(op: String, uid: Int, packageName: String, active: Boolean) {
+        update()
+    }
+
+    override fun onOpNoted(
+        code: String,
+        uid: Int,
+        packageName: String,
+        attributionTag: String?,
+        flags: Int,
+        result: Int
+    ) {
+        update()
+    }
+
+    /** Returns all op names for all permissions in a list of permission groups. */
+    private fun getOpNames(permissionGroupNames: List<String>?) =
+        permissionGroupNames
+            ?.flatMap { group -> PermissionMapping.getPlatformPermissionNamesOfGroup(group) }
+            ?.mapNotNull { permName -> AppOpsManager.permissionToOp(permName) }
+            ?.toSet()
+            ?: setOf()
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionGroup.java b/PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionGroup.java
index 555ba8d..eed017d 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionGroup.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionGroup.java
@@ -24,6 +24,9 @@
 import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.app.AppOpsManager.OPSTR_LEGACY_STORAGE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP;
+
+import static com.android.permissioncontroller.permission.utils.Utils.isHealthPermissionUiEnabled;
 
 import android.Manifest;
 import android.app.ActivityManager;
@@ -337,6 +340,8 @@
                     & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;
 
             final String appOp = PLATFORM_PACKAGE_NAME.equals(requestedPermissionInfo.packageName)
+                    || (isHealthPermissionUiEnabled() && HEALTH_PERMISSION_GROUP.equals(
+                    requestedPermissionInfo.group))
                     ? AppOpsManager.permissionToOp(requestedPermissionInfo.name) : null;
 
             final boolean appOpAllowed;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt
index 8bbefda..3c87f0b 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt
@@ -42,8 +42,11 @@
     val hasInstallToRuntimeSplit: Boolean,
     val specialLocationGrant: Boolean?
 ) {
-    constructor(pI: LightPackageInfo, pGI: LightPermGroupInfo, perms: Map<String, LightPermission>):
-        this(pI, pGI, perms, false, null)
+    constructor(
+        pI: LightPackageInfo,
+        pGI: LightPermGroupInfo,
+        perms: Map<String, LightPermission>
+    ) : this(pI, pGI, perms, false, null)
 
     /**
      * All unrestricted permissions. Usually restricted permissions are ignored
@@ -164,6 +167,16 @@
     val isRevokeWhenRequested = permissions.any { it.value.isRevokeWhenRequested }
 
     /**
+     * Whether any of this App Permission Groups permissions are fixed by the user
+     */
+    val isUserFixed = foreground.isUserFixed || background.isUserFixed
+
+    /**
+     * Whether any of this App Permission Group's permissions are set by the user
+     */
+    val isUserSet = foreground.isUserSet || background.isUserSet
+
+    /**
      * A subset of the AppPermissionGroup, representing either the background or foreground permissions
      * of the full group.
      *
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightInstallSourceInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightInstallSourceInfo.kt
new file mode 100644
index 0000000..68fdf87
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightInstallSourceInfo.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.permission.model.livedatatypes
+
+/**
+ * A lighter version of the system's InstallSourceInfo class, containing select information about
+ * the install source.
+ *
+ * @param installingPackageName The package name of the install source (usually the app store)
+ */
+class LightInstallSourceInfo(val installingPackageName: String?) {
+
+    companion object {
+        val UNKNOWN_INSTALL_SOURCE = LightInstallSourceInfo(null)
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt
index cb3c6ac..fd7d82d 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermission.kt
@@ -16,6 +16,7 @@
 
 package com.android.permissioncontroller.permission.model.livedatatypes
 
+import android.content.pm.PackageInfo
 import android.content.pm.PackageManager
 import android.content.pm.PermissionInfo
 import com.android.permissioncontroller.permission.utils.PermissionMapping.isRuntimePlatformPermission
@@ -71,6 +72,20 @@
     val isOneTime = flags and PackageManager.FLAG_PERMISSION_ONE_TIME != 0
     /** Whether this permission is an instant app permission */
     val isInstantPerm = permInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_INSTANT != 0
+    /** Whether this permission is implicitly added to the package */
+    val isImplicit: Boolean by lazy {
+        var implicit = false
+        for ((permName, permFlags) in
+        pkgInfo.requestedPermissions.zip(pkgInfo.requestedPermissionsFlags)) {
+            if (permName == permInfo.name &&
+                (permFlags and PackageInfo.REQUESTED_PERMISSION_IMPLICIT) != 0
+            ) {
+                implicit = true
+                break
+            }
+        }
+        implicit
+    }
     /** Whether this permission is a runtime only permission */
     val isRuntimeOnly =
         permInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY != 0
@@ -124,4 +139,4 @@
         if (isAutoRevoked) append(", AutoRevoked")
         if (isSelectedLocationAccuracy) append(", SelectedLocationAccuracy")
     }
-}
\ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/SafetyLabelInfo.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/SafetyLabelInfo.kt
new file mode 100644
index 0000000..2c26ad0
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/SafetyLabelInfo.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.permission.model.livedatatypes
+
+import com.android.permission.safetylabel.SafetyLabel
+
+/**
+ * A wrapping class for [SafetyLabel] class that includes the install source package name
+ *
+ * @param safetyLabel The resulting [SafetyLabel], or null if none found
+ * @param installSourcePackageName The package name of the install source for the APK and safety
+ * label(usually the app store)
+ */
+class SafetyLabelInfo(val safetyLabel: SafetyLabel?, val installSourcePackageName: String?) {
+
+    companion object {
+        /** Default definition of unavailable or no safety label found */
+        val UNAVAILABLE = SafetyLabelInfo(null, null)
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightPackageOps.kt b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightPackageOps.kt
new file mode 100644
index 0000000..87426eb
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightPackageOps.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.permission.model.livedatatypes.v31
+
+import android.app.AppOpsManager.OPSTR_READ_WRITE_HEALTH_DATA
+import android.app.AppOpsManager.OP_FLAG_SELF
+import android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED
+import android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY
+import android.app.AppOpsManager.PackageOps
+import android.app.AppOpsManager.opToPermission
+import android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP
+import android.os.UserHandle
+import com.android.permissioncontroller.permission.utils.PermissionMapping.getGroupOfPlatformPermission
+
+/**
+ * Light version of [PackageOps] class, tracking the last permission access for system permission
+ * groups.
+ */
+data class LightPackageOps(
+    /** Name of the package. */
+    val packageName: String,
+    /** [UserHandle] running the package. */
+    val userHandle: UserHandle,
+    /**
+     * Mapping of permission group name to the last access time of any op backing a permission in
+     * the group.
+     */
+    val lastPermissionGroupAccessTimesMs: Map<String, Long>
+) {
+    constructor(
+        ops: Set<String>,
+        packageOps: PackageOps
+    ) : this(
+        packageOps.packageName,
+        UserHandle.getUserHandleForUid(packageOps.uid),
+        createLastPermissionGroupAccessTimesMap(ops, packageOps))
+
+    /** Companion object for [LightPackageOps]. */
+    companion object {
+        /** Flags to use for querying an op's last access time. */
+        private const val OPS_LAST_ACCESS_FLAGS =
+            OP_FLAG_SELF or OP_FLAG_TRUSTED_PROXIED or OP_FLAG_TRUSTED_PROXY
+
+        /** Creates a mapping from permission group to the last time it was accessed. */
+        private fun createLastPermissionGroupAccessTimesMap(
+                opNames: Set<String>,
+                packageOps: PackageOps
+        ): Map<String, Long> {
+            val lastAccessTimeMs = mutableMapOf<String, Long>()
+            // Add keys for all permissions groups covered by the provided ops, regardless of
+            // whether they have been observed recently.
+            for (permissionGroup in opNames.mapNotNull { getPermissionGroupForOp(it) }.toSet()) {
+                lastAccessTimeMs[permissionGroup] = -1
+            }
+
+            for (opEntry in packageOps.ops) {
+                val permissionGroupOfOp = getPermissionGroupForOp(opEntry.opStr) ?: continue
+                lastAccessTimeMs[permissionGroupOfOp] =
+                    maxOf(
+                        lastAccessTimeMs[permissionGroupOfOp] ?: -1,
+                        opEntry.getLastAccessTime(OPS_LAST_ACCESS_FLAGS))
+            }
+
+            return lastAccessTimeMs
+        }
+
+        /** Returns the permission group for the permission that the provided op backs, if any. */
+        private fun getPermissionGroupForOp(opName: String): String? {
+            // The OPSTR_READ_WRITE_HEALTH_DATA is a special case as unlike other ops, it does not
+            // map to a single permission. However it is safe to retrieve a permission group for it,
+            // as all permissions it maps to, map to the same permission group
+            // HEALTH_PERMISSION_GROUP.
+            if (opName == OPSTR_READ_WRITE_HEALTH_DATA) {
+                return HEALTH_PERMISSION_GROUP
+            }
+
+            return opToPermission(opName)?.let { getGroupOfPlatformPermission(it) }
+        }
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/v31/AppPermissionUsage.java b/PermissionController/src/com/android/permissioncontroller/permission/model/v31/AppPermissionUsage.java
index 8622ef3..b7cddac 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/v31/AppPermissionUsage.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/v31/AppPermissionUsage.java
@@ -125,16 +125,6 @@
         return lastAccessTime;
     }
 
-    public long getAccessCount() {
-        long accessCount = 0;
-        final int permissionCount = mGroupUsages.size();
-        for (int i = 0; i < permissionCount; i++) {
-            final GroupUsage permission = mGroupUsages.get(i);
-            accessCount += permission.getAccessCount();
-        }
-        return accessCount;
-    }
-
     public @NonNull List<GroupUsage> getGroupUsages() {
         return mGroupUsages;
     }
@@ -163,62 +153,6 @@
             return lastAccessAggregate((op) -> op.getLastAccessTime(PRIVACY_HUB_FLAGS));
         }
 
-        public long getLastAccessForegroundTime() {
-            if (mLastUsage == null) {
-                return 0;
-            }
-
-            return lastAccessAggregate((op) -> op.getLastAccessForegroundTime(PRIVACY_HUB_FLAGS));
-        }
-
-        public long getLastAccessBackgroundTime() {
-            if (mLastUsage == null) {
-                return 0;
-            }
-
-            return lastAccessAggregate((op) -> op.getLastAccessBackgroundTime(PRIVACY_HUB_FLAGS));
-        }
-
-        public long getForegroundAccessCount() {
-            if (mHistoricalUsage == null) {
-                return 0;
-            }
-
-            return extractAggregate((HistoricalOp op)
-                    -> op.getForegroundAccessCount(PRIVACY_HUB_FLAGS));
-        }
-
-        public long getBackgroundAccessCount() {
-            if (mHistoricalUsage == null) {
-                return 0;
-            }
-
-            return extractAggregate((HistoricalOp op)
-                    -> op.getBackgroundAccessCount(PRIVACY_HUB_FLAGS));
-        }
-
-        public long getAccessCount() {
-            if (mHistoricalUsage == null) {
-                return 0;
-            }
-
-            return extractAggregate((HistoricalOp op) ->
-                    op.getForegroundAccessCount(PRIVACY_HUB_FLAGS)
-                            + op.getBackgroundAccessCount(PRIVACY_HUB_FLAGS)
-            );
-        }
-
-        /**
-         * Get the last access duration.
-         */
-        public long getLastAccessDuration() {
-            if (mLastUsage == null) {
-                return 0;
-            }
-            return lastAccessAggregate(
-                    (op) -> op.getLastDuration(AppOpsManager.OP_FLAGS_ALL_TRUSTED));
-        }
-
         /**
          * Get the access duration.
          */
@@ -508,11 +442,12 @@
                 }
 
                 AttributionLabelledGroupUsage build() {
+                    ArrayList<String> attributionTagsList = new ArrayList<>();
+                    attributionTagsList.addAll(mAttributionTags);
                     return new AttributionLabelledGroupUsage(mLabel,
                             mAppPermissionGroup,
-                            new ArrayList<String>() {{
-                                addAll(mAttributionTags);
-                            }}, mDiscreteAccessTime);
+                            attributionTagsList,
+                            mDiscreteAccessTime);
                 }
             }
         }
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/v31/PermissionUsages.java b/PermissionController/src/com/android/permissioncontroller/permission/model/v31/PermissionUsages.java
index a804479..6e37973 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/v31/PermissionUsages.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/v31/PermissionUsages.java
@@ -20,8 +20,10 @@
 import static android.Manifest.permission.RECORD_AUDIO;
 import static android.app.AppOpsManager.OPSTR_PHONE_CALL_CAMERA;
 import static android.app.AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE;
+import static android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP;
 
 import static com.android.permissioncontroller.Constants.OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO;
+import static com.android.permissioncontroller.permission.utils.Utils.isHealthPermissionUiEnabled;
 
 import android.app.AppOpsManager;
 import android.app.AppOpsManager.HistoricalOps;
@@ -233,7 +235,9 @@
             for (int groupIdx = 0; groupIdx < groupCount; groupIdx++) {
                 final PermissionGroup group = groups.get(groupIdx);
                 // Filter out third party permissions
-                if (!group.getDeclaringPackage().equals(Utils.OS_PKG)) {
+                if (!(group.getDeclaringPackage().equals(Utils.OS_PKG)
+                        || (isHealthPermissionUiEnabled() && HEALTH_PERMISSION_GROUP.equals(
+                        group.getName())))) {
                     continue;
                 }
 
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java b/PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java
index e0e124f..6d58d96 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/LocationAccessCheck.java
@@ -160,7 +160,7 @@
  */
 public class LocationAccessCheck {
     private static final String LOG_TAG = LocationAccessCheck.class.getSimpleName();
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
     private static final long DEFAULT_RENOTIFY_DURATION_MILLIS = DAYS.toMillis(90);
     private static final String ISSUE_ID_PREFIX = "bg_location_";
     private static final String ISSUE_TYPE_ID = "bg_location_privacy_issue";
@@ -410,6 +410,7 @@
     private void addLocationNotificationIfNeeded(@NonNull JobParameters params,
             @NonNull LocationAccessCheckJobService service) {
         if (!checkLocationAccessCheckEnabledAndUpdateEnabledTime()) {
+            Log.v(LOG_TAG, "LocationAccessCheck feature is not enabled.");
             service.jobFinished(params, false);
             return;
         }
@@ -419,11 +420,13 @@
                 if (currentTimeMillis() - mSharedPrefs.getLong(
                         KEY_LAST_LOCATION_ACCESS_NOTIFICATION_SHOWN, 0)
                         < getInBetweenNotificationsMillis()) {
+                    Log.v(LOG_TAG, "location notification interval is not enough.");
                     service.jobFinished(params, false);
                     return;
                 }
 
                 if (getCurrentlyShownNotificationLocked() != null) {
+                    Log.v(LOG_TAG, "already location notification exist.");
                     service.jobFinished(params, false);
                     return;
                 }
@@ -437,6 +440,7 @@
             } finally {
                 synchronized (sLock) {
                     service.mAddLocationNotificationIfNeededTask = null;
+                    Log.v(LOG_TAG, "LocationAccessCheck privacy job marked complete.");
                 }
             }
         }
@@ -447,6 +451,10 @@
         synchronized (sLock) {
             List<UserPackage> packages = getLocationUsersLocked(ops);
             ArraySet<UserPackage> alreadyNotifiedPackages = loadAlreadyNotifiedPackagesLocked();
+            if (DEBUG) {
+                Log.v(LOG_TAG, "location packages: " + packages);
+                Log.v(LOG_TAG, "already notified packages: " + alreadyNotifiedPackages);
+            }
             throwInterruptedExceptionIfTaskIsCanceled();
             // Send these issues to safety center
             if (isSafetyCenterBgLocationReminderEnabled()) {
@@ -462,6 +470,9 @@
                 throwInterruptedExceptionIfTaskIsCanceled();
 
                 if (packages.isEmpty()) {
+                    if (DEBUG) {
+                        Log.v(LOG_TAG, "No package found to send a notification");
+                    }
                     return;
                 }
 
@@ -1152,6 +1163,7 @@
 
         @Override
         public void onCreate() {
+            Log.v(LOG_TAG, "LocationAccessCheck privacy job is created");
             super.onCreate();
             mLocationAccessCheck = new LocationAccessCheck(this, () -> {
                 synchronized (sLock) {
@@ -1170,8 +1182,10 @@
          */
         @Override
         public boolean onStartJob(JobParameters params) {
+            Log.v(LOG_TAG, "LocationAccessCheck privacy job is started");
             synchronized (LocationAccessCheck.sLock) {
                 if (mAddLocationNotificationIfNeededTask != null) {
+                    Log.v(LOG_TAG, "LocationAccessCheck old job not completed yet.");
                     return false;
                 }
 
@@ -1192,6 +1206,7 @@
          */
         @Override
         public boolean onStopJob(JobParameters params) {
+            Log.v(LOG_TAG, "LocationAccessCheck privacy source onStopJob called.");
             AddLocationNotificationIfNeededTask task;
             synchronized (sLock) {
                 if (mAddLocationNotificationIfNeededTask == null) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java
index 2a090f6..5e03e72 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java
@@ -64,8 +64,8 @@
 import com.android.permissioncontroller.permission.utils.PermissionMapping;
 import com.android.permissioncontroller.permission.utils.UserSensitiveFlagsUtils;
 import com.android.permissioncontroller.permission.utils.Utils;
-import com.android.permissioncontroller.role.model.Role;
-import com.android.permissioncontroller.role.model.Roles;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.Roles;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlSerializer;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
index 5044fad..4ddbd68 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
@@ -23,6 +23,7 @@
 import android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
 import android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
 import android.content.pm.PermissionInfo
+import android.os.Build
 import android.os.Process.myUserHandle
 import android.permission.PermissionManager
 import android.util.Log
@@ -54,7 +55,11 @@
     private val LOG_TAG = RuntimePermissionsUpgradeController::class.java.simpleName
 
     // The latest version of the runtime permissions database
-    private val LATEST_VERSION = 9
+    private val LATEST_VERSION = if (SdkLevel.isAtLeastT()) {
+        10
+    } else {
+        9
+    }
 
     fun upgradeIfNeeded(context: Context, onComplete: Runnable) {
         val permissionManager = context.getSystemService(PermissionManager::class.java)
@@ -128,6 +133,8 @@
 
         val needBackgroundAppPermGroups = sdkUpgradedFromP && currentVersion <= 6
         val needAccessMediaAppPermGroups = !isNewUser && currentVersion <= 7
+        val needGrantedExternalStorage = currentVersion <= 9 && SdkLevel.isAtLeastT()
+        val isDeviceUpgrading = context.packageManager.isDeviceUpgrading
 
         // All data needed by this method.
         //
@@ -199,10 +206,11 @@
                     permGroupProviders = mutableListOf()
 
                     // Only load app-perm-groups needed for this upgrade
-                    if (needBackgroundAppPermGroups || needAccessMediaAppPermGroups) {
+                    if (needBackgroundAppPermGroups || needAccessMediaAppPermGroups ||
+                        needGrantedExternalStorage) {
                         for ((pkgName, _, requestedPerms, requestedPermFlags) in
                                 pkgInfoProvider.value!!) {
-                            var hasAccessMedia = false
+                            var requestsAccessMediaLocation = false
                             var hasGrantedExternalStorage = false
 
                             for ((perm, flags) in requestedPerms.zip(requestedPermFlags)) {
@@ -212,9 +220,10 @@
                                             permission_group.LOCATION, myUserHandle()])
                                 }
 
-                                if (needAccessMediaAppPermGroups) {
-                                    if (perm == permission.ACCESS_MEDIA_LOCATION) {
-                                        hasAccessMedia = true
+                                if (needAccessMediaAppPermGroups || needGrantedExternalStorage) {
+                                    if (needAccessMediaAppPermGroups &&
+                                        perm == permission.ACCESS_MEDIA_LOCATION) {
+                                        requestsAccessMediaLocation = true
                                     }
 
                                     if (perm == permission.READ_EXTERNAL_STORAGE &&
@@ -231,9 +240,20 @@
                                 else
                                     permission_group.STORAGE
 
-                            if (hasAccessMedia && hasGrantedExternalStorage) {
-                                permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName,
-                                        accessMediaLocationPermGroup, myUserHandle()])
+                            if (hasGrantedExternalStorage) {
+                                if (needGrantedExternalStorage) {
+                                    permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName,
+                                            permission_group.STORAGE, myUserHandle()])
+                                    if (SdkLevel.isAtLeastT()) {
+                                        permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName,
+                                                permission_group.READ_MEDIA_VISUAL, myUserHandle()])
+                                        permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName,
+                                                permission_group.READ_MEDIA_AURAL, myUserHandle()])
+                                    }
+                                } else if (requestsAccessMediaLocation) {
+                                    permGroupProviders!!.add(LightAppPermGroupLiveData[pkgName,
+                                            accessMediaLocationPermGroup, myUserHandle()])
+                                }
                             }
                         }
                     }
@@ -273,6 +293,12 @@
                             permission_group.STORAGE -> {
                                 storageGroups.add(group)
                             }
+                            permission_group.READ_MEDIA_AURAL -> {
+                                storageGroups.add(group)
+                            }
+                            permission_group.READ_MEDIA_VISUAL -> {
+                                storageGroups.add(group)
+                            }
                             permission_group.MICROPHONE -> {
                                 bgMicGroups.add(group)
                             }
@@ -311,7 +337,8 @@
 
         val (newVersion, upgradeExemptions, grants) = onUpgradeLockedDataLoaded(currentVersion,
                 upgradeData.pkgs, upgradeData.restrictedPermissions,
-                upgradeData.bgGroups, upgradeData.storageGroups, upgradeData.bgMicGroups)
+                upgradeData.bgGroups, upgradeData.storageGroups,
+                upgradeData.bgMicGroups, isDeviceUpgrading)
 
         // Do not run in parallel. Measurements have shown that this is slower than sequential
         for (exemption in (preinstalledAppExemptions union upgradeExemptions)) {
@@ -330,8 +357,9 @@
         pkgs: List<LightPackageInfo>,
         restrictedPermissions: Set<String>,
         bgApps: List<LightAppPermGroup>,
-        accessMediaApps: List<LightAppPermGroup>,
-        bgMicApps: List<LightAppPermGroup>
+        storageAndMediaAppPermGroups: List<LightAppPermGroup>,
+        bgMicApps: List<LightAppPermGroup>,
+        isDeviceUpgrading: Boolean
     ): Triple<Int, List<RestrictionExemption>, List<Grant>> {
         val exemptions = mutableListOf<RestrictionExemption>()
         val grants = mutableListOf<Grant>()
@@ -446,7 +474,7 @@
             if (!isNewUser) {
                 Log.i(LOG_TAG, "Expanding read storage to access media location")
 
-                for (appPermGroup in accessMediaApps) {
+                for (appPermGroup in storageAndMediaAppPermGroups) {
                     val perm = appPermGroup.permissions[permission.ACCESS_MEDIA_LOCATION]
                             ?: continue
 
@@ -470,6 +498,49 @@
             currentVersion = 9
         }
 
+        if (currentVersion == 9 && SdkLevel.isAtLeastT()) {
+            if (isNewUser) {
+                Log.i(LOG_TAG, "Not migrating STORAGE permissions to READ_MEDIA permissions as" +
+                    " this is a new user")
+            } else if (!isDeviceUpgrading) {
+                Log.i(LOG_TAG, "Not migrating STORAGE permissions to READ_MEDIA permissions as" +
+                    " this device is not performing an upgrade")
+            } else {
+                Log.i(LOG_TAG, "Migrating STORAGE permissions to READ_MEDIA permissions")
+
+                // Upon upgrading to platform 33, for all targetSdk>=33 apps, do the following:
+                // If STORAGE is granted, and the user has not set READ_MEDIA_AURAL or
+                // READ_MEDIA_VISUAL, grant READ_MEDIA_AURAL and READ_MEDIA_VISUAL
+                val storageAppPermGroups = storageAndMediaAppPermGroups.filter {
+                    it.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU &&
+                        it.permGroupInfo.name == permission_group.STORAGE &&
+                        it.isGranted && it.isUserSet
+                }
+                for (storageAppPermGroup in storageAppPermGroups) {
+                    val pkgName = storageAppPermGroup.packageInfo.packageName
+                    val auralAppPermGroup = storageAndMediaAppPermGroups.firstOrNull {
+                        it.packageInfo.packageName == pkgName &&
+                            it.permGroupInfo.name == permission_group.READ_MEDIA_AURAL &&
+                            !it.isUserSet && !it.isUserFixed
+                    }
+                    val visualAppPermGroup = storageAndMediaAppPermGroups.firstOrNull {
+                        it.packageInfo.packageName == pkgName &&
+                            it.permGroupInfo.name == permission_group.READ_MEDIA_VISUAL &&
+                            !it.permissions.filter { it.key != permission.ACCESS_MEDIA_LOCATION }
+                                .any { it.value.isUserSet || it.value.isUserFixed }
+                    }
+
+                    if (auralAppPermGroup != null) {
+                        grants.add(Grant(false, auralAppPermGroup))
+                    }
+                    if (visualAppPermGroup != null) {
+                        grants.add(Grant(false, visualAppPermGroup))
+                    }
+                }
+            }
+            currentVersion = 10
+        }
+
         // XXX: Add new upgrade steps above this point.
 
         return Triple(currentVersion, exemptions, grants)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/v33/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/permission/service/v33/TEST_MAPPING
index 1d0c94e..4205933 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/v33/TEST_MAPPING
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/v33/TEST_MAPPING
@@ -8,12 +8,25 @@
           "exclude-annotation": "com.android.bedstead.harrier.annotations.Postsubmit"
         }
       ]
+    },
+    {
+      "file_patterns": ["SafetyCenterQsTileService\\.kt"],
+      "name": "SafetyCenterFunctionalTestCases",
+      "options": [
+        {
+          "exclude-annotation": "com.android.bedstead.harrier.annotations.Postsubmit"
+        }
+      ]
     }
   ],
   "postsubmit": [
     {
       "file_patterns": ["SafetyCenterQsTileService\\.kt"],
       "name": "CtsSafetyCenterTestCases"
+    },
+    {
+      "file_patterns": ["SafetyCenterQsTileService\\.kt"],
+      "name": "SafetyCenterFunctionalTestCases"
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
index 6dd429b..393ed81 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
@@ -18,6 +18,8 @@
 
 import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission_group.READ_MEDIA_VISUAL;
+import static android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.CANCELED;
@@ -26,14 +28,20 @@
 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS;
 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY;
 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ONE_TIME;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_USER_SELECTED;
 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.LINKED_TO_SETTINGS;
+import static com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModel.APP_PERMISSION_REQUEST_CODE;
+import static com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModel.PHOTO_PICKER_REQUEST_CODE;
 import static com.android.permissioncontroller.permission.utils.Utils.getRequestMessage;
 
+import android.Manifest;
 import android.app.KeyguardManager;
 import android.content.Intent;
+import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.drawable.Icon;
+import android.icu.lang.UCharacter;
 import android.os.Bundle;
 import android.os.Process;
 import android.text.Annotation;
@@ -57,9 +65,9 @@
 import com.android.permissioncontroller.DeviceUtils;
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.permission.ui.auto.GrantPermissionsAutoViewHandler;
-import com.android.permissioncontroller.permission.ui.model.v31.GrantPermissionsViewModel;
-import com.android.permissioncontroller.permission.ui.model.v31.GrantPermissionsViewModel.RequestInfo;
-import com.android.permissioncontroller.permission.ui.model.v31.GrantPermissionsViewModelFactory;
+import com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModel;
+import com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModel.RequestInfo;
+import com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModelFactory;
 import com.android.permissioncontroller.permission.ui.wear.GrantPermissionsWearViewHandler;
 import com.android.permissioncontroller.permission.utils.KotlinUtils;
 import com.android.permissioncontroller.permission.utils.Utils;
@@ -69,6 +77,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Random;
 
 /**
@@ -83,7 +92,7 @@
             + "_REQUEST_ID";
     public static final String ANNOTATION_ID = "link";
 
-    public static final int NEXT_BUTTON = 11;
+    public static final int NEXT_BUTTON = 15;
     public static final int ALLOW_BUTTON = 0;
     public static final int ALLOW_ALWAYS_BUTTON = 1; // Used in auto
     public static final int ALLOW_FOREGROUND_BUTTON = 2;
@@ -95,6 +104,10 @@
     public static final int NO_UPGRADE_OT_BUTTON = 8; // one-time
     public static final int NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON = 9; // one-time
     public static final int LINK_TO_SETTINGS = 10;
+    public static final int ALLOW_ALL_PHOTOS_BUTTON = 11;
+    public static final int ALLOW_SELECTED_PHOTOS_BUTTON = 12;
+    public static final int ALLOW_MORE_SELECTED_PHOTOS_BUTTON = 13;
+    public static final int DONT_ALLOW_MORE_SELECTED_PHOTOS_BUTTON = 14;
 
     public static final int NEXT_LOCATION_DIALOG = 6;
     public static final int LOCATION_ACCURACY_LAYOUT = 0;
@@ -104,13 +117,11 @@
     public static final int DIALOG_WITH_FINE_LOCATION_ONLY = 4;
     public static final int DIALOG_WITH_COARSE_LOCATION_ONLY = 5;
 
-    public static final Map<String, Integer> PERMISSION_TO_BIT_SHIFT =
-            new HashMap<String, Integer>() {{
-                put(ACCESS_COARSE_LOCATION, 0);
-                put(ACCESS_FINE_LOCATION, 1);
-            }};
+    public static final Map<String, Integer> PERMISSION_TO_BIT_SHIFT = Map.of(
+            ACCESS_COARSE_LOCATION, 0,
+            ACCESS_FINE_LOCATION, 1);
 
-    private static final int APP_PERMISSION_REQUEST_CODE = 1;
+    public static final String INTENT_PHOTOS_SELECTED = "intent_extra_result";
 
     /**
      * A map of the currently shown GrantPermissionsActivity for this user, per package and task ID
@@ -155,6 +166,7 @@
     private int mCurrentRequestIdx = 0;
     private float mOriginalDimAmount;
     private View mRootView;
+    private int mStoragePermGroupIcon = R.drawable.ic_empty_icon;
 
     @Override
     public void onCreate(Bundle icicle) {
@@ -194,7 +206,7 @@
             // If this app is below the android T targetSdk, filter out the POST_NOTIFICATIONS
             // permission, if present
             mRequestedPermissions = GrantPermissionsViewModel.Companion
-                    .filterNotificationPermissionIfNeededSync(
+                    .filterPermissionsIfNeededSync(
                             mTargetPackage, mRequestedPermissions);
         }
 
@@ -281,6 +293,12 @@
             // Do not show screen dim until data is loaded
             window.setDimAmount(0f);
         }
+
+        PackageItemInfo storageGroupInfo =
+                Utils.getGroupInfo(Manifest.permission_group.STORAGE, this.getApplicationContext());
+        if (storageGroupInfo != null) {
+            mStoragePermGroupIcon = storageGroupInfo.icon;
+        }
     }
 
     /**
@@ -371,6 +389,15 @@
         if (info.getSendToSettingsImmediately()) {
             mViewModel.sendDirectlyToSettings(this, info.getGroupName());
             return;
+        } else if (info.getOpenPhotoPicker()) {
+            mViewModel.openPhotoPicker(this, GRANTED_USER_SELECTED);
+            return;
+        }
+
+        if (Utils.isHealthPermissionUiEnabled() && HEALTH_PERMISSION_GROUP.equals(
+                info.getGroupName())) {
+            mViewModel.handleHealthConnectPermissions(this);
+            return;
         }
 
         CharSequence appLabel = KotlinUtils.INSTANCE.getPackageLabel(getApplication(),
@@ -395,13 +422,16 @@
                 messageId = Utils.getUpgradeRequest(info.getGroupName());
                 break;
             case STORAGE_SUPERGROUP_MESSAGE_Q_TO_S:
-                icon = Icon.createWithResource(getPackageName(), R.drawable.perm_group_storage);
+                icon = Icon.createWithResource(getPackageName(), mStoragePermGroupIcon);
                 messageId = R.string.permgrouprequest_storage_q_to_s;
                 break;
             case STORAGE_SUPERGROUP_MESSAGE_PRE_Q:
-                icon = Icon.createWithResource(getPackageName(), R.drawable.perm_group_storage);
+                icon = Icon.createWithResource(getPackageName(), mStoragePermGroupIcon);
                 messageId = R.string.permgrouprequest_storage_pre_q;
                 break;
+            case MORE_PHOTOS_MESSAGE:
+                messageId = R.string.permgrouprequest_more_photos;
+                break;
         }
 
         CharSequence message = getRequestMessage(appLabel, mTargetPackage,
@@ -458,6 +488,16 @@
             setTitle(message);
         }
 
+        CharSequence permissionRationaleMessage = null;
+        if (info.getShowPermissionRationale()) {
+            String permissionGroupLabel =
+                    KotlinUtils.INSTANCE.getPermGroupLabel(this, info.getGroupName())
+                            .toString();
+
+            permissionRationaleMessage = getString(R.string.permission_rationale_message_template,
+                    UCharacter.toLowerCase(permissionGroupLabel));
+        }
+
         ArrayList<Integer> idxs = new ArrayList<>();
         mButtonVisibilities = new boolean[info.getButtonVisibilities().size()];
         for (int i = 0; i < info.getButtonVisibilities().size(); i++) {
@@ -473,7 +513,8 @@
         }
 
         mViewHandler.updateUi(info.getGroupName(), mTotalRequests, mCurrentRequestIdx, icon,
-                message, detailMessage, mButtonVisibilities, mLocationVisibilities);
+                message, detailMessage, permissionRationaleMessage, mButtonVisibilities,
+                mLocationVisibilities);
         if (showingNewGroup) {
             mCurrentRequestIdx++;
         }
@@ -486,6 +527,7 @@
         }
     }
 
+    // LINT.IfChange(dispatchTouchEvent)
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         View rootView = getWindow().getDecorView();
@@ -503,6 +545,7 @@
         }
         return super.dispatchTouchEvent(ev);
     }
+    // LINT.ThenChange(PermissionRationaleActivity.java:dispatchTouchEvent)
 
     @Override
     protected void onSaveInstanceState(@NonNull Bundle outState) {
@@ -534,30 +577,21 @@
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
         Consumer<Intent> callback = mViewModel.getActivityResultCallback();
-
-        if (requestCode == APP_PERMISSION_REQUEST_CODE && callback != null) {
-            callback.accept(data);
-            mViewModel.setActivityResultCallback(null);
+        if (callback == null || (requestCode != APP_PERMISSION_REQUEST_CODE
+                && requestCode != PHOTO_PICKER_REQUEST_CODE)) {
+            return;
         }
+        if (requestCode == PHOTO_PICKER_REQUEST_CODE) {
+            data = new Intent("").putExtra(INTENT_PHOTOS_SELECTED, resultCode == RESULT_OK);
+        }
+        callback.accept(data);
+        mViewModel.setActivityResultCallback(null);
     }
 
     @Override
     public void onPermissionGrantResult(String name,
             @GrantPermissionsViewHandler.Result int result) {
-        if (checkKgm(name, null, result)) {
-            return;
-        }
-
-        if (name == null || name.equals(mPreMergeShownGroupName)) {
-            mPreMergeShownGroupName = null;
-        }
-
-        logGrantPermissionActivityButtons(name, null, result);
-        mViewModel.onPermissionGrantResult(name, null, result);
-        showNextRequest();
-        if (result == CANCELED) {
-            setResultAndFinish();
-        }
+        onPermissionGrantResult(name, null, result);
     }
 
     @Override
@@ -567,10 +601,16 @@
             return;
         }
 
-        if (name != null && name.equals(mPreMergeShownGroupName)) {
+        if (name == null || name.equals(mPreMergeShownGroupName)) {
             mPreMergeShownGroupName = null;
         }
 
+        if (Objects.equals(READ_MEDIA_VISUAL, name)
+                && result == GrantPermissionsViewHandler.GRANTED_USER_SELECTED) {
+            mViewModel.openPhotoPicker(this, result);
+            return;
+        }
+
         logGrantPermissionActivityButtons(name, affectedForegroundPermissions, result);
         mViewModel.onPermissionGrantResult(name, affectedForegroundPermissions, result);
         showNextRequest();
@@ -580,6 +620,11 @@
     }
 
     @Override
+    public void onPermissionRationaleClicked(String groupName) {
+        mViewModel.showPermissionRationaleActivity(this, groupName);
+    }
+
+    @Override
     public void onBackPressed() {
         if (mViewHandler == null) {
             return;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsViewHandler.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsViewHandler.java
index d5dc22e..6aaa836 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsViewHandler.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsViewHandler.java
@@ -36,7 +36,7 @@
 public interface GrantPermissionsViewHandler {
     @Retention(SOURCE)
     @IntDef({CANCELED, GRANTED_ALWAYS, GRANTED_FOREGROUND_ONLY, DENIED, DENIED_DO_NOT_ASK_AGAIN,
-            GRANTED_ONE_TIME})
+            GRANTED_ONE_TIME, GRANTED_USER_SELECTED, DENIED_MORE_PHOTOS})
     @interface Result {}
     int LINKED_TO_SETTINGS = -2;
     int CANCELED = -1;
@@ -45,6 +45,8 @@
     int DENIED = 2;
     int DENIED_DO_NOT_ASK_AGAIN = 3;
     int GRANTED_ONE_TIME = 4;
+    int GRANTED_USER_SELECTED = 5;
+    int DENIED_MORE_PHOTOS = 6;
 
     /**
      * Listener interface for getting notified when the user responds to a
@@ -55,6 +57,8 @@
 
         void onPermissionGrantResult(String groupName, List<String> affectedForegroundPermissions,
                 @Result int result);
+
+        void onPermissionRationaleClicked(String groupName);
     }
 
     /**
@@ -83,11 +87,15 @@
      * @param message the message to display the user
      * @param detailMessage another message to display to the user. This clarifies "message" in more
      *                      detail
+     * @param permissionRationaleMessage another message to display to the user. This message lets
+     *                                   users know developer stated data usages for the requested
+     *                                   permission
      * @param buttonVisibilities visibilities for each button
      * @param locationVisibilities visibilities for location options
      */
     void updateUi(String groupName, int groupCount, int groupIndex, Icon icon,
-            CharSequence message, CharSequence detailMessage, boolean[] buttonVisibilities,
+            CharSequence message, CharSequence detailMessage,
+            CharSequence permissionRationaleMessage, boolean[] buttonVisibilities,
             boolean[] locationVisibilities);
 
     /**
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
index 91ca823..f00090a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.permissioncontroller.permission.ui;
 
+import static android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
 import static com.android.permissioncontroller.Constants.ACTION_MANAGE_AUTO_REVOKE;
@@ -71,6 +72,7 @@
 import com.android.permissioncontroller.permission.ui.handheld.PermissionAppsFragment;
 import com.android.permissioncontroller.permission.ui.handheld.v31.PermissionDetailsWrapperFragment;
 import com.android.permissioncontroller.permission.ui.handheld.v31.PermissionUsageV2WrapperFragment;
+import com.android.permissioncontroller.permission.ui.handheld.v34.AppDataSharingUpdatesFragment;
 import com.android.permissioncontroller.permission.ui.legacy.AppPermissionActivity;
 import com.android.permissioncontroller.permission.ui.television.TvUnusedAppsFragment;
 import com.android.permissioncontroller.permission.ui.wear.AppPermissionsFragmentWear;
@@ -364,6 +366,13 @@
                     return;
                 }
 
+                if (Utils.isHealthPermissionUiEnabled() && permissionGroupName
+                                .equals(HEALTH_PERMISSION_GROUP)) {
+                    Utils.navigateToHealthConnectSettings(this);
+                    finishAfterTransition();
+                    return;
+                }
+
                 if (DeviceUtils.isAuto(this)) {
                     androidXFragment =
                             AutoPermissionAppsFragment.newInstance(permissionGroupName, sessionId);
@@ -434,6 +443,21 @@
                 }
             } break;
 
+            case Intent.ACTION_REVIEW_APP_DATA_SHARING_UPDATES: {
+                // TODO(b/261652173): Add flagging and fold Sdk check into flag.
+                if (SdkLevel.isAtLeastU()) {
+                    if (DeviceUtils.isAuto(this) || DeviceUtils.isWear(this)
+                            || DeviceUtils.isTelevision(this)) {
+                        Log.e(LOG_TAG, "ACTION_REVIEW_APP_DATA_SHARING_UPDATES is not "
+                                + "supported on this device type");
+                        finishAfterTransition();
+                        return;
+                    }
+                    setNavGraph(AppDataSharingUpdatesFragment.Companion.createArgs(sessionId),
+                            R.id.app_data_sharing_updates);
+                }
+            } break;
+
             default: {
                 Log.w(LOG_TAG, "Unrecognized action " + action);
                 finishAfterTransition();
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/ReviewOngoingUsageActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/ReviewOngoingUsageActivity.java
index 6dfceb9..05a75f5 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/ReviewOngoingUsageActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/ReviewOngoingUsageActivity.java
@@ -25,8 +25,8 @@
 import androidx.annotation.NonNull;
 
 import com.android.permissioncontroller.DeviceUtils;
-import com.android.permissioncontroller.permission.ui.handheld.v31.DashboardUtilsKt;
 import com.android.permissioncontroller.permission.ui.handheld.v31.ReviewOngoingUsageWrapperFragment;
+import com.android.permissioncontroller.permission.utils.KotlinUtils;
 
 /**
  * A dialog listing the currently uses of camera, microphone, and location.
@@ -40,8 +40,8 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        if (!DashboardUtilsKt.shouldShowCameraMicIndicators()
-                && !DashboardUtilsKt.shouldShowLocationIndicators()) {
+        if (!KotlinUtils.INSTANCE.shouldShowCameraMicIndicators()
+                && !KotlinUtils.INSTANCE.shouldShowLocationIndicators()) {
             finishAfterTransition();
             return;
         }
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java
index 0973d1d..cf58f1e 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java
@@ -23,7 +23,6 @@
 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND;
 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__DENIED;
 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME;
-import static com.android.permissioncontroller.permission.ui.handheld.v31.DashboardUtilsKt.is7DayToggleEnabled;
 
 import static java.util.concurrent.TimeUnit.DAYS;
 
@@ -146,7 +145,7 @@
         if (SdkLevel.isAtLeastS()) {
             mPermissionUsages = new PermissionUsages(getContext());
 
-            long aggregateDataFilterBeginDays = is7DayToggleEnabled()
+            long aggregateDataFilterBeginDays = KotlinUtils.INSTANCE.is7DayToggleEnabled()
                     ? AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 :
                     AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_1;
 
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
index 194faff..6b09921 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
@@ -91,8 +91,11 @@
 
     @Override
     public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon,
-            CharSequence message, CharSequence detailMessage, boolean[] buttonVisibilities,
+            CharSequence message, CharSequence detailMessage,
+            CharSequence permissionRationaleMessage, boolean[] buttonVisibilities,
             boolean[] locationVisibilities) {
+        // permissionRationaleMessage ignored by auto
+
         mGroupName = groupName;
         mGroupCount = groupCount;
         mGroupIndex = groupIndex;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java
index eaa45fc..6821b0c 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java
@@ -17,6 +17,7 @@
 package com.android.permissioncontroller.permission.ui.handheld;
 
 import static android.Manifest.permission_group.STORAGE;
+import static android.app.Activity.RESULT_OK;
 
 import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
 import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
@@ -27,6 +28,7 @@
 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY;
 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY_FOREGROUND;
 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__GRANT_FINE_LOCATION;
+import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__PHOTOS_SELECTED;
 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__REVOKE_FINE_LOCATION;
 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED;
 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN;
@@ -38,7 +40,6 @@
 import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack;
 
 import android.app.ActionBar;
-import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.role.RoleManager;
@@ -109,6 +110,7 @@
     private @NonNull RadioButton mAllowForegroundButton;
     private @NonNull RadioButton mAskOneTimeButton;
     private @NonNull RadioButton mAskButton;
+    private @NonNull RadioButton mSelectButton;
     private @NonNull RadioButton mDenyButton;
     private @NonNull RadioButton mDenyForegroundButton;
     private @NonNull View mLocationAccuracy;
@@ -203,6 +205,7 @@
         if (mIsStorageGroup) {
             mViewModel.getFullStorageStateLiveData().observe(this, this::setSpecialStorageState);
         }
+        mViewModel.registerPhotoPickerResultIfNeeded(this);
 
         mRoleManager = Utils.getSystemServiceSafe(getContext(), RoleManager.class);
     }
@@ -251,6 +254,7 @@
         mAllowForegroundButton = root.requireViewById(R.id.allow_foreground_only_radio_button);
         mAskOneTimeButton = root.requireViewById(R.id.ask_one_time_radio_button);
         mAskButton = root.requireViewById(R.id.ask_radio_button);
+        mSelectButton = root.requireViewById(R.id.select_photos_radio_button);
         mDenyButton = root.requireViewById(R.id.deny_radio_button);
         mDenyForegroundButton = root.requireViewById(R.id.deny_foreground_radio_button);
         mDivider = root.requireViewById(R.id.two_target_divider);
@@ -378,8 +382,20 @@
                     APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ASK_EVERY_TIME);
             setResult(DENIED);
         });
+        mSelectButton.setOnClickListener((v) -> {
+            int buttonPressed =
+                    APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__PHOTOS_SELECTED;
+            mViewModel.openPhotoPicker(result -> {
+                if (result == RESULT_OK) {
+                    mViewModel.requestChange(false, this, this, ChangeRequest.PHOTOS_SELECTED,
+                            buttonPressed);
+                } else {
+                    // Reset the button state to what is was previously
+                    setRadioButtonsState(states);
+                }
+            });
+        });
         mDenyButton.setOnClickListener((v) -> {
-
             if (mViewModel.getFullStorageStateLiveData().getValue() != null
                     && !mViewModel.getFullStorageStateLiveData().getValue().isLegacy()) {
                 mViewModel.setAllFilesAccess(false);
@@ -416,6 +432,12 @@
         setButtonState(mAskButton, states.get(ButtonType.ASK));
         setButtonState(mDenyButton, states.get(ButtonType.DENY));
         setButtonState(mDenyForegroundButton, states.get(ButtonType.DENY_FOREGROUND));
+        setButtonState(mSelectButton, states.get(ButtonType.SELECT_PHOTOS));
+        if (mSelectButton.getVisibility() == View.VISIBLE) {
+            mAllowButton.setText(R.string.app_permission_button_allow_all_photos);
+        } else {
+            mAllowButton.setText(R.string.app_permission_button_allow);
+        }
 
         ButtonState locationAccuracyState = states.get(ButtonType.LOCATION_ACCURACY);
         if (!locationAccuracyState.isShown()) {
@@ -485,7 +507,7 @@
         Intent intent = new Intent()
                 .putExtra(EXTRA_RESULT_PERMISSION_INTERACTED, mPermGroupName)
                 .putExtra(EXTRA_RESULT_PERMISSION_RESULT, result);
-        getActivity().setResult(Activity.RESULT_OK, intent);
+        getActivity().setResult(RESULT_OK, intent);
     }
 
     private void setDetail(Pair<Integer, Integer> detailResIds) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
index 9bb49be..568d7f2 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java
@@ -24,7 +24,6 @@
 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__DENIED;
 import static com.android.permissioncontroller.hibernation.HibernationPolicyKt.isHibernationEnabled;
 import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack;
-import static com.android.permissioncontroller.permission.ui.handheld.v31.DashboardUtilsKt.is7DayToggleEnabled;
 
 import static java.util.concurrent.TimeUnit.DAYS;
 
@@ -183,7 +182,7 @@
             Context context = getPreferenceManager().getContext();
             mPermissionUsages = new PermissionUsages(context);
 
-            long aggregateDataFilterBeginDays = is7DayToggleEnabled()
+            long aggregateDataFilterBeginDays = KotlinUtils.INSTANCE.is7DayToggleEnabled()
                     ? AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 :
                     AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_1;
 
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt
index 61bb65e..883ff86 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.kt
@@ -45,16 +45,20 @@
 import com.airbnb.lottie.LottieDrawable
 import com.android.modules.utils.build.SdkLevel
 import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALL_PHOTOS_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALWAYS_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_FOREGROUND_BUTTON
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_MORE_SELECTED_PHOTOS_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ONE_TIME_BUTTON
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_SELECTED_PHOTOS_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.COARSE_RADIO_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_AND_DONT_ASK_AGAIN_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_BOTH_LOCATIONS
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_COARSE_LOCATION_ONLY
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_FINE_LOCATION_ONLY
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DONT_ALLOW_MORE_SELECTED_PHOTOS_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.FINE_RADIO_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LOCATION_ACCURACY_LAYOUT
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_BUTTON
@@ -67,9 +71,11 @@
 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.CANCELED
 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED
 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN
+import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_MORE_PHOTOS
 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS
 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY
 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ONE_TIME
+import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_USER_SELECTED
 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.ResultListener
 
 class GrantPermissionsViewHandlerImpl(
@@ -89,6 +95,7 @@
     private var groupIcon: Icon? = null
     private var groupMessage: CharSequence? = null
     private var detailMessage: CharSequence? = null
+    private var permissionRationaleMessage: CharSequence? = null
     private val buttonVisibilities = BooleanArray(NEXT_BUTTON) { false }
     private val locationVisibilities = BooleanArray(NEXT_LOCATION_DIALOG) { false }
     private var selectedPrecision: Int = 0
@@ -104,6 +111,8 @@
     private var iconView: ImageView? = null
     private var messageView: TextView? = null
     private var detailMessageView: TextView? = null
+    private var permissionRationaleView: View? = null
+    private var permissionRationaleMessageView: TextView? = null
     private var buttons: Array<Button?> = arrayOfNulls(NEXT_BUTTON)
     private var locationViews: Array<View?> = arrayOfNulls(NEXT_LOCATION_DIALOG)
     private var rootView: ViewGroup? = null
@@ -115,6 +124,8 @@
         arguments.putParcelable(ARG_GROUP_ICON, groupIcon)
         arguments.putCharSequence(ARG_GROUP_MESSAGE, groupMessage)
         arguments.putCharSequence(ARG_GROUP_DETAIL_MESSAGE, detailMessage)
+        arguments.putCharSequence(ARG_GROUP_PERMISSION_RATIONALE_MESSAGE,
+            permissionRationaleMessage)
         arguments.putBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES, buttonVisibilities)
         arguments.putBooleanArray(ARG_DIALOG_LOCATION_VISIBILITIES, locationVisibilities)
         arguments.putInt(ARG_DIALOG_SELECTED_PRECISION, selectedPrecision)
@@ -127,6 +138,8 @@
         groupCount = savedInstanceState.getInt(ARG_GROUP_COUNT)
         groupIndex = savedInstanceState.getInt(ARG_GROUP_INDEX)
         detailMessage = savedInstanceState.getCharSequence(ARG_GROUP_DETAIL_MESSAGE)
+        permissionRationaleMessage =
+            savedInstanceState.getCharSequence(ARG_GROUP_PERMISSION_RATIONALE_MESSAGE)
         setButtonVisibilities(savedInstanceState.getBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES))
         setLocationVisibilities(savedInstanceState.getBooleanArray(
             ARG_DIALOG_LOCATION_VISIBILITIES))
@@ -136,14 +149,15 @@
     }
 
     override fun updateUi(
-        groupName: String,
+        groupName: String?,
         groupCount: Int,
         groupIndex: Int,
         icon: Icon?,
         message: CharSequence?,
         detailMessage: CharSequence?,
-        buttonVisibilities: BooleanArray,
-        locationVisibilities: BooleanArray
+        permissionRationaleMessage: CharSequence?,
+        buttonVisibilities: BooleanArray?,
+        locationVisibilities: BooleanArray?
     ) {
 
         this.groupName = groupName
@@ -152,6 +166,7 @@
         groupIcon = icon
         groupMessage = message
         this.detailMessage = detailMessage
+        this.permissionRationaleMessage = permissionRationaleMessage
         setButtonVisibilities(buttonVisibilities)
         setLocationVisibilities(locationVisibilities)
 
@@ -164,6 +179,7 @@
     private fun updateAll() {
         updateDescription()
         updateDetailDescription()
+        updatePermissionRationale()
         updateButtons()
         updateLocationVisibilities()
 
@@ -206,6 +222,10 @@
         detailMessageView!!.movementMethod = LinkMovementMethod.getInstance()
         iconView = rootView.findViewById(R.id.permission_icon)
 
+        permissionRationaleView = rootView.findViewById(R.id.permission_rationale_container)
+        permissionRationaleMessageView = rootView.findViewById(R.id.permission_rationale_message)
+        permissionRationaleView!!.setOnClickListener(this)
+
         val buttons = arrayOfNulls<Button>(NEXT_BUTTON)
         val numButtons = BUTTON_RES_ID_TO_NUM.size()
         for (i in 0 until numButtons) {
@@ -299,6 +319,16 @@
         }
     }
 
+    private fun updatePermissionRationale() {
+        val message = permissionRationaleMessage
+        if (message == null || message.isEmpty()) {
+            permissionRationaleView!!.visibility = View.GONE
+        } else {
+            permissionRationaleMessageView!!.text = message
+            permissionRationaleView!!.visibility = View.VISIBLE
+        }
+    }
+
     private fun updateButtons() {
         for (i in 0 until BUTTON_RES_ID_TO_NUM.size()) {
             val pos = BUTTON_RES_ID_TO_NUM.valueAt(i)
@@ -406,6 +436,11 @@
     override fun onClick(view: View) {
         val id = view.id
 
+        if (id == R.id.permission_rationale_container) {
+            resultListener.onPermissionRationaleClicked(groupName)
+            return
+        }
+
         if (id == R.id.permission_location_accuracy_radio_fine) {
             (locationViews[FINE_RADIO_BUTTON] as RadioButton).isChecked = true
             selectedPrecision = FINE_RADIO_BUTTON
@@ -445,6 +480,7 @@
         }
 
         when (BUTTON_RES_ID_TO_NUM.get(id, -1)) {
+            ALLOW_ALL_PHOTOS_BUTTON,
             ALLOW_BUTTON -> {
                 view.performAccessibilityAction(
                     AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null)
@@ -469,6 +505,16 @@
                 resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions,
                     GRANTED_ONE_TIME)
             }
+            ALLOW_SELECTED_PHOTOS_BUTTON, ALLOW_MORE_SELECTED_PHOTOS_BUTTON -> {
+                view.performAccessibilityAction(
+                    AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null)
+                resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions,
+                    GRANTED_USER_SELECTED)
+            }
+            DONT_ALLOW_MORE_SELECTED_PHOTOS_BUTTON -> {
+                resultListener.onPermissionGrantResult(groupName, affectedForegroundPermissions,
+                    DENIED_MORE_PHOTOS)
+            }
             DENY_BUTTON, NO_UPGRADE_BUTTON, NO_UPGRADE_OT_BUTTON -> {
                 view.performAccessibilityAction(
                     AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null)
@@ -498,6 +544,7 @@
     }
 
     companion object {
+        private val TAG = GrantPermissionsViewHandlerImpl::class.java.simpleName
 
         const val ARG_GROUP_NAME = "ARG_GROUP_NAME"
         const val ARG_GROUP_COUNT = "ARG_GROUP_COUNT"
@@ -505,6 +552,8 @@
         const val ARG_GROUP_ICON = "ARG_GROUP_ICON"
         const val ARG_GROUP_MESSAGE = "ARG_GROUP_MESSAGE"
         private const val ARG_GROUP_DETAIL_MESSAGE = "ARG_GROUP_DETAIL_MESSAGE"
+        private const val ARG_GROUP_PERMISSION_RATIONALE_MESSAGE =
+            "ARG_GROUP_PERMISSION_RATIONALE_MESSAGE"
         private const val ARG_DIALOG_BUTTON_VISIBILITIES = "ARG_DIALOG_BUTTON_VISIBILITIES"
         private const val ARG_DIALOG_LOCATION_VISIBILITIES = "ARG_DIALOG_LOCATION_VISIBILITIES"
         private const val ARG_DIALOG_SELECTED_PRECISION = "ARG_DIALOG_SELECTED_PRECISION"
@@ -533,6 +582,14 @@
                 NO_UPGRADE_OT_BUTTON)
             BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_one_time_and_dont_ask_again_button,
                 NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON)
+            BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_all_photos_button,
+                ALLOW_ALL_PHOTOS_BUTTON)
+            BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_selected_photos_button,
+                ALLOW_SELECTED_PHOTOS_BUTTON)
+            BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_more_selected_photos_button,
+                ALLOW_MORE_SELECTED_PHOTOS_BUTTON)
+            BUTTON_RES_ID_TO_NUM.put(R.id.permission_dont_allow_more_selected_photos_button,
+                DONT_ALLOW_MORE_SELECTED_PHOTOS_BUTTON)
 
             LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy, LOCATION_ACCURACY_LAYOUT)
             LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_radio_fine,
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java
index 6245c92..8e3192e 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java
@@ -20,7 +20,6 @@
 import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
 import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
 import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack;
-import static com.android.permissioncontroller.permission.ui.handheld.v31.DashboardUtilsKt.shouldShowPermissionsDashboard;
 
 import android.app.Application;
 import android.content.Intent;
@@ -39,6 +38,7 @@
 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity;
 import com.android.permissioncontroller.permission.ui.UnusedAppsFragment;
 import com.android.permissioncontroller.permission.ui.model.ManageStandardPermissionsViewModel;
+import com.android.permissioncontroller.permission.utils.KotlinUtils;
 import com.android.permissioncontroller.permission.utils.StringUtils;
 import com.android.permissioncontroller.permission.utils.Utils;
 import com.android.settingslib.widget.FooterPreference;
@@ -123,7 +123,7 @@
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         super.onCreateOptionsMenu(menu, inflater);
 
-        if (shouldShowPermissionsDashboard()) {
+        if (KotlinUtils.INSTANCE.shouldShowPermissionsDashboard()) {
             menu.add(Menu.NONE, MENU_PERMISSION_USAGE, Menu.NONE, R.string.permission_usage_title);
         }
     }
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java
index 1d92a6e..90d7204 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java
@@ -22,7 +22,6 @@
 import static com.android.permissioncontroller.permission.ui.Category.ASK;
 import static com.android.permissioncontroller.permission.ui.Category.DENIED;
 import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack;
-import static com.android.permissioncontroller.permission.ui.handheld.v31.DashboardUtilsKt.shouldShowPermissionsDashboard;
 
 import android.Manifest;
 import android.app.ActionBar;
@@ -197,7 +196,7 @@
             updateMenu(mViewModel.getShouldShowSystemLiveData().getValue());
         }
 
-        if (shouldShowPermissionsDashboard()) {
+        if (KotlinUtils.INSTANCE.shouldShowPermissionsDashboard()) {
             menu.add(Menu.NONE, MENU_PERMISSION_USAGE, Menu.NONE, R.string.permission_usage_title);
         }
 
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionControlPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionControlPreference.java
index 31ef791..814adf6 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionControlPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionControlPreference.java
@@ -16,6 +16,8 @@
 
 package com.android.permissioncontroller.permission.ui.handheld;
 
+import static android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP;
+
 import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME;
 import static com.android.permissioncontroller.permission.ui.handheld.AppPermissionFragment.GRANT_CATEGORY;
@@ -221,6 +223,11 @@
                     Utils.navigateToAppNotificationSettings(mContext, mPackageName, mUser);
                     return true;
                 }
+                if (Utils.isHealthPermissionUiEnabled()
+                        && mPermGroupName.equals(HEALTH_PERMISSION_GROUP)) {
+                    Utils.navigateToAppHealthConnectSettings(mContext, mPackageName, mUser);
+                    return true;
+                }
                 Bundle args = new Bundle();
                 args.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName);
                 args.putString(Intent.EXTRA_PERMISSION_GROUP_NAME, mPermGroupName);
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.java
index 9569bae..658a82a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.java
@@ -36,10 +36,10 @@
 
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup;
-import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel;
-import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel.PermissionSummary;
-import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel.PermissionTarget;
-import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel.SummaryMessage;
+import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel;
+import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel.PermissionSummary;
+import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel.PermissionTarget;
+import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel.SummaryMessage;
 import com.android.permissioncontroller.permission.utils.LocationUtils;
 import com.android.permissioncontroller.permission.utils.Utils;
 import com.android.settingslib.RestrictedLockUtils;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ReviewPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ReviewPermissionsFragment.java
index a6f74c8..5e5c221 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ReviewPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ReviewPermissionsFragment.java
@@ -57,9 +57,9 @@
 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup;
 import com.android.permissioncontroller.permission.model.livedatatypes.LightPermission;
 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity;
-import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionViewModelFactory;
-import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel;
-import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel.PermissionTarget;
+import com.android.permissioncontroller.permission.ui.model.ReviewPermissionViewModelFactory;
+import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel;
+import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel.PermissionTarget;
 import com.android.permissioncontroller.permission.utils.KotlinUtils;
 import com.android.permissioncontroller.permission.utils.Utils;
 
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt
index 19b19f6..5b92dd3 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/DashboardUtils.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.icu.util.Calendar
 import android.os.Build
-import android.provider.DeviceConfig
 import android.text.format.DateFormat.getMediumDateFormat
 import android.text.format.DateFormat.getTimeFormat
 import android.util.Pair
@@ -30,58 +29,12 @@
 import com.android.permissioncontroller.permission.utils.StringUtils
 import java.util.Locale
 
-/** Whether to show the Permissions Hub.  */
-private const val PROPERTY_PERMISSIONS_HUB_2_ENABLED = "permissions_hub_2_enabled"
-
-/** Whether to show the mic and camera icons.  */
-const val PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled"
-
-/** Whether to show the location indicators. */
-const val PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled"
-
-/* Whether location accuracy feature is enabled */
-const val PROPERTY_LOCATION_ACCURACY_ENABLED = "location_accuracy_enabled"
-
-/** Whether to show 7-day toggle in privacy hub.  */
-private const val PRIVACY_DASHBOARD_7_DAY_TOGGLE = "privacy_dashboard_7_day_toggle"
-
-/* Default location precision */
-const val PROPERTY_LOCATION_PRECISION = "location_precision"
-
 const val SECONDS = 1
 const val MINUTES = 2
 const val HOURS = 3
 const val DAYS = 4
 
 /**
- * Whether the Permissions Hub 2 flag is enabled
- *
- * @return whether the flag is enabled
- */
-fun isPermissionsHub2FlagEnabled(): Boolean {
-    return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-        PROPERTY_PERMISSIONS_HUB_2_ENABLED, false)
-}
-/**
- * Whether to show the Permissions Dashboard
- *
- * @return whether to show the Permissions Dashboard.
- */
-fun shouldShowPermissionsDashboard(): Boolean {
-    return isPermissionsHub2FlagEnabled()
-}
-
-/**
- * Whether we should enable the 7-day toggle in privacy dashboard
- *
- * @return whether the flag is enabled
- */
-fun is7DayToggleEnabled(): Boolean {
-    return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-            PRIVACY_DASHBOARD_7_DAY_TOGGLE, false)
-}
-
-/**
  * Whether to show the subattribution in the Permissions Dashboard
  *
  * @return whether to show subattribution in the Permissions Dashboard.
@@ -91,62 +44,6 @@
 }
 
 /**
- * Whether the Camera and Mic Icons are enabled by flag.
- *
- * @return whether the Camera and Mic Icons are enabled.
- */
-fun isCameraMicIconsFlagEnabled(): Boolean {
-    return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-        PROPERTY_CAMERA_MIC_ICONS_ENABLED, true)
-}
-
-/**
- * Whether to show Camera and Mic Icons. They should be shown if the permission hub, or the icons
- * specifically, are enabled.
- *
- * @return whether to show the icons.
- */
-fun shouldShowCameraMicIndicators(): Boolean {
-    return isCameraMicIconsFlagEnabled() || isPermissionsHub2FlagEnabled()
-}
-
-/**
- * Whether the location indicators are enabled by flag.
- *
- * @return whether the location indicators are enabled by flag.
- */
-fun isLocationIndicatorsFlagEnabled(): Boolean {
-    return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-            PROPERTY_LOCATION_INDICATORS_ENABLED, false)
-}
-
-/**
- * Whether to show the location indicators. The location indicators are enable if the
- * permission hub, or location indicator specifically are enabled.
- */
-fun shouldShowLocationIndicators(): Boolean {
-    return isLocationIndicatorsFlagEnabled() || isPermissionsHub2FlagEnabled()
-}
-
-/**
- * Whether the location accuracy feature is enabled
- */
-fun isLocationAccuracyEnabled(): Boolean {
-    return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-        PROPERTY_LOCATION_ACCURACY_ENABLED, true)
-}
-
-/**
- * Default state of location precision
- * true: default is FINE.
- * false: default is COARSE.
- */
-fun getDefaultPrecision(): Boolean {
-    return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-            PROPERTY_LOCATION_PRECISION, true)
-}
-
-/**
  * Build a string representing the given time if it happened on the current day and the date
  * otherwise.
  *
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionDetailsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionDetailsFragment.java
index 07bcf68..8850152 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionDetailsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionDetailsFragment.java
@@ -18,7 +18,6 @@
 
 import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
 import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
-import static com.android.permissioncontroller.permission.ui.handheld.v31.DashboardUtilsKt.is7DayToggleEnabled;
 
 import android.app.ActionBar;
 import android.app.Activity;
@@ -130,7 +129,7 @@
             mShowSystem =
                     getArguments().getBoolean(ManagePermissionsActivity.EXTRA_SHOW_SYSTEM, false);
             mShow7Days =
-                    is7DayToggleEnabled()
+                    KotlinUtils.INSTANCE.is7DayToggleEnabled()
                             && getArguments()
                                     .getBoolean(ManagePermissionsActivity.EXTRA_SHOW_7_DAYS, false);
             mSessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID);
@@ -258,7 +257,7 @@
                 menu.add(Menu.NONE, MENU_SHOW_SYSTEM, Menu.NONE, R.string.menu_show_system);
         mHideSystemMenu =
                 menu.add(Menu.NONE, MENU_HIDE_SYSTEM, Menu.NONE, R.string.menu_hide_system);
-        if (is7DayToggleEnabled()) {
+        if (KotlinUtils.INSTANCE.is7DayToggleEnabled()) {
             mShow7DaysDataMenu =
                     menu.add(
                             Menu.NONE,
@@ -316,7 +315,8 @@
                 break;
             case MENU_SHOW_7_DAYS_DATA:
             case MENU_SHOW_24_HOURS_DATA:
-                mShow7Days = is7DayToggleEnabled() && itemId == MENU_SHOW_7_DAYS_DATA;
+                mShow7Days = KotlinUtils.INSTANCE.is7DayToggleEnabled()
+                        && itemId == MENU_SHOW_7_DAYS_DATA;
                 updateUI();
                 updateMenu();
                 break;
@@ -433,11 +433,10 @@
                             historyPreferenceData.getAppIcon(),
                             historyPreferenceData.getPreferenceTitle(),
                             historyPreferenceData.getPermissionGroup(),
-                            DateFormat.getTimeFormat(getContext())
-                                    .format(historyPreferenceData.getAccessEndTime()),
+                            historyPreferenceData.getAccessStartTime(),
+                            historyPreferenceData.getAccessEndTime(),
                             historyPreferenceData.getSummaryText(),
                             historyPreferenceData.getShowingAttribution(),
-                            historyPreferenceData.getAccessTimeList(),
                             historyPreferenceData.getAttributionTags(),
                             i == historyPreferenceDataList.size() - 1,
                             historyPreferenceData.getSessionId());
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java
index 7cbb8c5..8a723be 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionHistoryPreference.java
@@ -28,6 +28,7 @@
 import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
+import android.text.format.DateFormat;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -48,7 +49,6 @@
 import com.android.permissioncontroller.permission.utils.Utils;
 
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Objects;
 
 /**
@@ -62,10 +62,10 @@
     private final UserHandle mUserHandle;
     private final String mPackageName;
     private final String mPermissionGroup;
-    private final String mAccessTime;
+    private final long mAccessStartTime;
+    private final long mAccessEndTime;
     private final Drawable mAppIcon;
     private final String mTitle;
-    private final List<Long> mAccessTimeList;
     private final ArrayList<String> mAttributionTags;
     private final boolean mIsLastUsage;
     private final Intent mIntent;
@@ -80,9 +80,10 @@
             @NonNull UserHandle userHandle, @NonNull String pkgName,
             @NonNull Drawable appIcon,
             @NonNull String preferenceTitle,
-            @NonNull String permissionGroup, @NonNull String accessTime,
+            @NonNull String permissionGroup,
+            @NonNull long accessStartTime,
+            @NonNull long accessEndTime,
             @Nullable CharSequence summaryText, boolean showingAttribution,
-            @NonNull List<Long> accessTimeList,
             @NonNull ArrayList<String> attributionTags, boolean isLastUsage, long sessionId) {
         super(context);
         mContext = context;
@@ -91,11 +92,11 @@
         mUserHandle = userHandle;
         mPackageName = pkgName;
         mPermissionGroup = permissionGroup;
-        mAccessTime = accessTime;
+        mAccessStartTime = accessStartTime;
+        mAccessEndTime = accessEndTime;
         mAppIcon = appIcon;
         mTitle = preferenceTitle;
         mWidgetIcon = null;
-        mAccessTimeList = accessTimeList;
         mAttributionTags = attributionTags;
         mIsLastUsage = isLastUsage;
         mSessionId = sessionId;
@@ -135,7 +136,7 @@
         widgetFrameParent.setGravity(Gravity.TOP);
 
         TextView permissionHistoryTime = widget.findViewById(R.id.permission_history_time);
-        permissionHistoryTime.setText(mAccessTime);
+        permissionHistoryTime.setText(DateFormat.getTimeFormat(mContext).format(mAccessEndTime));
 
         ImageView permissionIcon = widget.findViewById(R.id.permission_history_icon);
         permissionIcon.setImageDrawable(mAppIcon);
@@ -204,8 +205,8 @@
         intent.setPackage(mPackageName);
         intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, mPermissionGroup);
         intent.putExtra(Intent.EXTRA_ATTRIBUTION_TAGS, mAttributionTags.toArray(new String[0]));
-        intent.putExtra(Intent.EXTRA_START_TIME, mAccessTimeList.get(mAccessTimeList.size() - 1));
-        intent.putExtra(Intent.EXTRA_END_TIME, mAccessTimeList.get(0));
+        intent.putExtra(Intent.EXTRA_START_TIME, mAccessStartTime);
+        intent.putExtra(Intent.EXTRA_END_TIME, mAccessEndTime);
         intent.putExtra(IntentCompat.EXTRA_SHOWING_ATTRIBUTION, mShowingAttribution);
 
         ResolveInfo resolveInfo = mUserPackageManager.resolveActivity(intent,
@@ -233,8 +234,8 @@
         viewUsageIntent.putExtra(Intent.EXTRA_ATTRIBUTION_TAGS,
                 mAttributionTags.toArray(new String[0]));
         viewUsageIntent.putExtra(Intent.EXTRA_START_TIME,
-                mAccessTimeList.get(mAccessTimeList.size() - 1));
-        viewUsageIntent.putExtra(Intent.EXTRA_END_TIME, mAccessTimeList.get(0));
+                mAccessStartTime);
+        viewUsageIntent.putExtra(Intent.EXTRA_END_TIME, mAccessEndTime);
         viewUsageIntent.putExtra(IntentCompat.EXTRA_SHOWING_ATTRIBUTION, showingAttribution);
         viewUsageIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageV2Fragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageV2Fragment.java
index cd285f9..39cf050 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageV2Fragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageV2Fragment.java
@@ -22,11 +22,9 @@
 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SEE_OTHER_PERMISSIONS_CLICKED;
 import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED;
 import static com.android.permissioncontroller.PermissionControllerStatsLog.write;
-import static com.android.permissioncontroller.permission.ui.handheld.v31.DashboardUtilsKt.is7DayToggleEnabled;
 
+import android.Manifest;
 import android.app.ActionBar;
-import android.app.Activity;
-import android.app.role.RoleManager;
 import android.content.Context;
 import android.os.Build;
 import android.os.Bundle;
@@ -34,8 +32,6 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.lifecycle.ViewModelProvider;
 import androidx.preference.Preference;
@@ -45,33 +41,36 @@
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.permissioncontroller.R;
-import com.android.permissioncontroller.permission.model.legacy.PermissionApps;
-import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage;
-import com.android.permissioncontroller.permission.model.v31.PermissionUsages;
 import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader;
-import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageViewModel;
-import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageViewModel.PermissionGroupWithUsageCount;
-import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageViewModelFactory;
+import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageViewModelNew;
 import com.android.permissioncontroller.permission.utils.KotlinUtils;
-import com.android.permissioncontroller.permission.utils.Utils;
 import com.android.settingslib.HelpUtils;
 
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 
 /** The main page for the privacy dashboard. */
+// TODO(b/257317510): Remove "V2" suffix.
 @RequiresApi(Build.VERSION_CODES.S)
-public class PermissionUsageV2Fragment extends SettingsWithLargeHeader
-        implements PermissionUsages.PermissionsUsagesChangeCallback {
+public class PermissionUsageV2Fragment extends SettingsWithLargeHeader {
+
+    private static final Map<String, Integer> PERMISSION_GROUP_ORDER =
+            Map.of(
+                    Manifest.permission_group.LOCATION, 0,
+                    Manifest.permission_group.CAMERA, 1,
+                    Manifest.permission_group.MICROPHONE, 2);
+    private static final int DEFAULT_ORDER = 3;
 
     // Pie chart in this screen will be the first child.
     // Hence we use PERMISSION_GROUP_ORDER + 1 here.
     private static final int PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT =
-            PermissionUsageViewModel.Companion.getPERMISSION_GROUP_ORDER().size() + 1;
+            PERMISSION_GROUP_ORDER.size() + 1;
     private static final int EXPAND_BUTTON_ORDER = 999;
-
+    /** Map to represent ordering for permission groups in the permissions usage UI. */
     private static final String KEY_SESSION_ID = "_session_id";
+
     private static final String SESSION_ID_KEY =
             PermissionUsageV2Fragment.class.getName() + KEY_SESSION_ID;
 
@@ -79,24 +78,15 @@
     private static final int MENU_SHOW_24_HOURS_DATA = Menu.FIRST + 5;
     private static final int MENU_REFRESH = Menu.FIRST + 6;
 
-    @NonNull private PermissionUsages mPermissionUsages;
-    @Nullable private List<AppPermissionUsage> mAppPermissionUsages = new ArrayList<>();
+    private PermissionUsageViewModelNew mViewModel;
 
-    private PermissionUsageViewModel mViewModel;
-
-    private boolean mShowSystem;
     private boolean mHasSystemApps;
     private MenuItem mShowSystemMenu;
     private MenuItem mHideSystemMenu;
-    private boolean mShow7Days;
     private MenuItem mShow7DaysDataMenu;
     private MenuItem mShow24HoursDataMenu;
     private boolean mOtherExpanded;
 
-    private boolean mFinishedInitialLoad;
-
-    @NonNull private RoleManager mRoleManager;
-
     private PermissionUsageGraphicPreference mGraphic;
 
     /** Unique Id of a request */
@@ -112,13 +102,16 @@
             mSessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID);
         }
 
-        mFinishedInitialLoad = false;
+        PermissionUsageViewModelNew.PermissionUsageViewModelFactory factory =
+                new PermissionUsageViewModelNew.PermissionUsageViewModelFactory(
+                        getActivity().getApplication(), this, new Bundle());
+        mViewModel = new ViewModelProvider(this, factory).get(PermissionUsageViewModelNew.class);
 
         // By default, do not show system app usages.
-        mShowSystem = false;
+        mViewModel.updateShowSystem(false);
 
         // By default, show permission usages for the past 24 hours.
-        mShow7Days = false;
+        mViewModel.updateShow7Days(false);
 
         // Start out with 'other' permissions not expanded.
         mOtherExpanded = false;
@@ -130,14 +123,9 @@
             ab.setDisplayHomeAsUpEnabled(true);
         }
 
-        Context context = getPreferenceManager().getContext();
-        mPermissionUsages = new PermissionUsages(context);
-        mRoleManager = Utils.getSystemServiceSafe(context, RoleManager.class);
-
-        PermissionUsageViewModelFactory factory = new PermissionUsageViewModelFactory(mRoleManager);
-        mViewModel = new ViewModelProvider(this, factory).get(PermissionUsageViewModel.class);
-
-        reloadData();
+        mViewModel.getPermissionUsagesUiLiveData().observe(this, this::updateUI);
+        mViewModel.getShowSystemLiveData().observe(this, this::updateShowSystem);
+        mViewModel.getShow7DaysLiveData().observe(this, this::updateShow7Days);
     }
 
     @Override
@@ -219,7 +207,7 @@
                     menu.add(Menu.NONE, MENU_HIDE_SYSTEM, Menu.NONE, R.string.menu_hide_system);
         }
 
-        if (is7DayToggleEnabled()) {
+        if (KotlinUtils.INSTANCE.is7DayToggleEnabled()) {
             mShow7DaysDataMenu =
                     menu.add(
                             Menu.NONE,
@@ -240,7 +228,6 @@
                 menu.add(Menu.NONE, MENU_REFRESH, Menu.NONE, R.string.permission_usage_refresh);
         refresh.setIcon(R.drawable.ic_refresh);
         refresh.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
-        updateMenu();
     }
 
     @Override
@@ -255,50 +242,24 @@
                         PERMISSION_USAGE_FRAGMENT_INTERACTION,
                         mSessionId,
                         PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED);
-                // fall through
+                mViewModel.updateShowSystem(true);
+                break;
             case MENU_HIDE_SYSTEM:
-                mShowSystem = itemId == MENU_SHOW_SYSTEM;
-                // We already loaded all data, so don't reload
-                updateUI();
-                updateMenu();
+                mViewModel.updateShowSystem(false);
                 break;
             case MENU_SHOW_7_DAYS_DATA:
+                mViewModel.updateShow7Days(KotlinUtils.INSTANCE.is7DayToggleEnabled());
+                break;
             case MENU_SHOW_24_HOURS_DATA:
-                mShow7Days = is7DayToggleEnabled() && itemId == MENU_SHOW_7_DAYS_DATA;
-                updateUI();
-                updateMenu();
+                mViewModel.updateShow7Days(false);
                 break;
             case MENU_REFRESH:
-                reloadData();
+                // TODO(b/257314894): What should happen on refresh?
                 break;
         }
         return super.onOptionsItemSelected(item);
     }
 
-    private void updateMenu() {
-        if (mHasSystemApps) {
-            mShowSystemMenu.setVisible(!mShowSystem);
-            mHideSystemMenu.setVisible(mShowSystem);
-        }
-
-        if (mShow7DaysDataMenu != null) {
-            mShow7DaysDataMenu.setVisible(!mShow7Days);
-        }
-
-        if (mShow24HoursDataMenu != null) {
-            mShow24HoursDataMenu.setVisible(mShow7Days);
-        }
-    }
-
-    @Override
-    public void onPermissionUsagesChanged() {
-        if (mPermissionUsages.getUsages().isEmpty()) {
-            return;
-        }
-        mAppPermissionUsages = new ArrayList<>(mPermissionUsages.getUsages());
-        updateUI();
-    }
-
     @Override
     public int getEmptyViewString() {
         return R.string.no_permission_usages;
@@ -312,8 +273,26 @@
         }
     }
 
-    private void updateUI() {
-        if (mAppPermissionUsages.isEmpty() || getActivity() == null) {
+    private void updateShowSystem(boolean showSystem) {
+        if (mHasSystemApps) {
+            mShowSystemMenu.setVisible(!showSystem);
+            mHideSystemMenu.setVisible(showSystem);
+        }
+    }
+
+    private void updateShow7Days(boolean show7Days) {
+        if (mShow7DaysDataMenu != null) {
+            mShow7DaysDataMenu.setVisible(!show7Days);
+        }
+
+        if (mShow24HoursDataMenu != null) {
+            mShow24HoursDataMenu.setVisible(show7Days);
+        }
+    }
+
+    private void updateUI(
+            PermissionUsageViewModelNew.PermissionUsagesUiData permissionUsagesUiData) {
+        if (getActivity() == null) {
             return;
         }
         Context context = getActivity();
@@ -332,45 +311,60 @@
                     PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT);
         }
         screen.setOnExpandButtonClickListener(() -> {
-            write(PERMISSION_USAGE_FRAGMENT_INTERACTION, mSessionId,
+            write(
+                    PERMISSION_USAGE_FRAGMENT_INTERACTION,
+                    mSessionId,
                     PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SEE_OTHER_PERMISSIONS_CLICKED);
         });
+        boolean displayShowSystemToggle = permissionUsagesUiData.getDisplayShowSystemToggle();
+        Map<String, Integer> permissionGroupWithUsageCounts =
+                permissionUsagesUiData.getPermissionGroupsWithUsageCount();
+        List<Map.Entry<String, Integer>> permissionGroupWithUsageCountsEntries =
+                new ArrayList(permissionGroupWithUsageCounts.entrySet());
 
-        PermissionUsageViewModel.PermissionUsagesUiData permissionUsageUiData =
-                mViewModel.buildPermissionUsagesUiData(
-                        mAppPermissionUsages, mShow7Days, mShowSystem, getContext());
-        Map<String, Integer> permissionGroupUsageCounts =
-                permissionUsageUiData.getPermissionGroupUsageCounts();
-        ArrayList<PermissionApps.PermissionApp> permissionApps =
-                permissionUsageUiData.getPermissionApps();
-        boolean displayShowSystemToggle = permissionUsageUiData.getDisplayShowSystemToggle();
-        List<PermissionUsageViewModel.PermissionGroupWithUsageCount>
-                orderedPermissionGroupsWithUsageCount =
-                        permissionUsageUiData.getOrderedPermissionGroupsWithUsageCount();
+        permissionGroupWithUsageCountsEntries.sort(Comparator.comparing(
+                (Map.Entry<String, Integer> permissionGroupWithUsageCount) ->
+                        PERMISSION_GROUP_ORDER.getOrDefault(
+                                permissionGroupWithUsageCount.getKey(),
+                                DEFAULT_ORDER))
+                .thenComparing(
+                    (Map.Entry<String, Integer> permissionGroupWithUsageCount) ->
+                        KotlinUtils.INSTANCE
+                                .getPermGroupLabel(
+                                        context,
+                                        permissionGroupWithUsageCount
+                                                .getKey())
+                                .toString()));
 
         if (mHasSystemApps != displayShowSystemToggle) {
             mHasSystemApps = displayShowSystemToggle;
             getActivity().invalidateOptionsMenu();
         }
 
-        mGraphic = new PermissionUsageGraphicPreference(context, mShow7Days);
+        mGraphic =
+                new PermissionUsageGraphicPreference(
+                        context, permissionUsagesUiData.getShow7DaysUsage());
         screen.addPreference(mGraphic);
-        mGraphic.setUsages(permissionGroupUsageCounts);
+
+        mGraphic.setUsages(permissionGroupWithUsageCounts);
 
         // Add the preference header.
         PreferenceCategory category = new PreferenceCategory(context);
         screen.addPreference(category);
         CharSequence advancedInfoSummary =
-                getAdvancedInfoSummaryString(context, orderedPermissionGroupsWithUsageCount);
+                getAdvancedInfoSummaryString(context, permissionGroupWithUsageCountsEntries);
         screen.setSummary(advancedInfoSummary);
 
-        addUIContent(context, orderedPermissionGroupsWithUsageCount, permissionApps, category);
+        addUIContent(
+                context,
+                permissionGroupWithUsageCountsEntries,
+                category,
+                permissionUsagesUiData.getShowSystemAppPermissions(),
+                permissionUsagesUiData.getShow7DaysUsage());
     }
 
     private CharSequence getAdvancedInfoSummaryString(
-            Context context,
-            List<PermissionUsageViewModel.PermissionGroupWithUsageCount>
-                    permissionGroupWithUsageCounts) {
+            Context context, List<Map.Entry<String, Integer>> permissionGroupWithUsageCounts) {
         int size = permissionGroupWithUsageCounts.size();
         if (size <= PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT - 1) {
             return "";
@@ -381,18 +375,18 @@
             String permGroupName =
                     permissionGroupWithUsageCounts
                             .get(PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT - 1)
-                            .getPermGroup();
+                            .getKey();
             return KotlinUtils.INSTANCE.getPermGroupLabel(context, permGroupName);
         }
 
         String permGroupName1 =
                 permissionGroupWithUsageCounts
                         .get(PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT - 1)
-                        .getPermGroup();
+                        .getKey();
         String permGroupName2 =
                 permissionGroupWithUsageCounts
                         .get(PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT)
-                        .getPermGroup();
+                        .getKey();
         CharSequence permGroupLabel1 =
                 KotlinUtils.INSTANCE.getPermGroupLabel(context, permGroupName1);
         CharSequence permGroupLabel2 =
@@ -420,44 +414,24 @@
     /** Use the usages and permApps that are previously constructed to add UI content to the page */
     private void addUIContent(
             Context context,
-            List<PermissionGroupWithUsageCount>
-                    permissionGroupWithUsageCounts,
-            ArrayList<PermissionApps.PermissionApp> permApps,
-            PreferenceCategory category) {
-        new PermissionApps.AppDataLoader(
-                context,
-                () -> {
-                    for (int i = 0; i < permissionGroupWithUsageCounts.size(); i++) {
-                        PermissionGroupWithUsageCount
-                                permissionGroupWithUsageCount =
-                                        permissionGroupWithUsageCounts.get(i);
-                        PermissionUsageV2ControlPreference permissionUsagePreference =
-                                new PermissionUsageV2ControlPreference(
-                                context,
-                                permissionGroupWithUsageCount.getPermGroup(),
-                                permissionGroupWithUsageCount.getAppCount(),
-                                        mShowSystem,
-                                        mSessionId,
-                                        mShow7Days);
-                        category.addPreference(permissionUsagePreference);
-                    }
-
-            setLoading(false, true);
-            mFinishedInitialLoad = true;
-            setProgressBarVisible(false);
-
-            Activity activity = getActivity();
-            if (activity != null) {
-                mPermissionUsages.stopLoader(activity.getLoaderManager());
-            }
-        }).execute(permApps.toArray(new PermissionApps.PermissionApp[0]));
-    }
-
-    /** Reloads the data to show. */
-    private void reloadData() {
-        mViewModel.loadPermissionUsages(getActivity().getLoaderManager(), mPermissionUsages, this);
-        if (mFinishedInitialLoad) {
-            setProgressBarVisible(true);
+            List<Map.Entry<String, Integer>> permissionGroupWithUsageCounts,
+            PreferenceCategory category,
+            boolean showSystem,
+            boolean show7Days) {
+        for (int i = 0; i < permissionGroupWithUsageCounts.size(); i++) {
+            Map.Entry<String, Integer> permissionGroupWithUsageCount =
+                    permissionGroupWithUsageCounts.get(i);
+            PermissionUsageV2ControlPreference permissionUsagePreference =
+                    new PermissionUsageV2ControlPreference(
+                            context,
+                            permissionGroupWithUsageCount.getKey(),
+                            permissionGroupWithUsageCount.getValue(),
+                            showSystem,
+                            mSessionId,
+                            show7Days);
+            category.addPreference(permissionUsagePreference);
         }
+
+        setLoading(false, true);
     }
 }
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFragment.kt
new file mode 100644
index 0000000..f1d4aaf
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFragment.kt
@@ -0,0 +1,35 @@
+package com.android.permissioncontroller.permission.ui.handheld.v34
+
+import android.os.Build
+import android.os.Bundle
+import androidx.annotation.RequiresApi
+import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID
+import com.android.permissioncontroller.permission.ui.handheld.SettingsWithHeader
+
+/** Fragment to display data sharing updates for installed apps. */
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+class AppDataSharingUpdatesFragment : SettingsWithHeader() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        // TODO(b/261914980): Update final strings.
+        setHeader(
+            /* icon= */ null,
+            "Data Sharing updates",
+            /* infoIntent= */ null,
+            /* userHandle= */ null)
+        super.onCreate(savedInstanceState)
+    }
+
+    /** Companion object for [AppDataSharingUpdatesFragment]. */
+    companion object {
+        /**
+         * Creates a [Bundle] with the arguments needed by this fragment.
+         *
+         * @param sessionId the current session ID
+         * @return a [Bundle] with all of the required args
+         */
+        fun createArgs(sessionId: Long) =
+                Bundle().apply {
+                    putLong(EXTRA_SESSION_ID, sessionId)
+                }
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt
new file mode 100644
index 0000000..10cf037
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.permission.ui.handheld.v34
+
+import android.app.Activity
+import android.os.Build
+import android.os.Bundle
+import android.text.method.LinkMovementMethod
+import android.transition.ChangeBounds
+import android.transition.TransitionManager
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.View.OnClickListener
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.view.animation.AnimationUtils
+import android.widget.Button
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.annotation.RequiresApi
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.ui.v34.PermissionRationaleViewHandler
+import com.android.permissioncontroller.permission.ui.v34.PermissionRationaleViewHandler.Result.Companion.CANCELLED
+
+/**
+ * Handheld implementation of [PermissionRationaleViewHandler]. Used for managing the presentation
+ * and user interaction of the "permission rationale" user interface.
+ */
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+class PermissionRationaleViewHandlerImpl(
+    private val mActivity: Activity,
+    private val resultListener: PermissionRationaleViewHandler.ResultListener
+) : PermissionRationaleViewHandler, OnClickListener {
+
+    private var groupName: String? = null
+    private var purposeMessage: CharSequence? = null
+    private var settingsMessage: CharSequence? = null
+    private var learnMoreMessage: CharSequence? = null
+
+    private var rootView: ViewGroup? = null
+    private var purposeMessageView: TextView? = null
+    private var settingsMessageView: TextView? = null
+    private var learnMoreMessageView: TextView? = null
+    private var backButton: Button? = null
+
+    override fun saveInstanceState(outState: Bundle) {
+        outState.putString(ARG_GROUP_NAME, groupName)
+        outState.putCharSequence(ARG_PURPOSE_MESSAGE, purposeMessage)
+        outState.putCharSequence(ARG_SETTINGS_MESSAGE, settingsMessage)
+        outState.putCharSequence(ARG_LEARN_MORE_MESSAGE, learnMoreMessage)
+    }
+
+    override fun loadInstanceState(savedInstanceState: Bundle) {
+        groupName = savedInstanceState.getString(ARG_GROUP_NAME)
+        purposeMessage = savedInstanceState.getCharSequence(ARG_PURPOSE_MESSAGE)
+        settingsMessage = savedInstanceState.getCharSequence(ARG_SETTINGS_MESSAGE)
+        learnMoreMessage = savedInstanceState.getCharSequence(ARG_LEARN_MORE_MESSAGE)
+    }
+
+    override fun updateUi(
+        groupName: String,
+        purposeMessage: CharSequence,
+        settingsMessage: CharSequence,
+        learnMoreMessage: CharSequence
+    ) {
+        this.groupName = groupName
+        this.purposeMessage = purposeMessage
+        this.settingsMessage = settingsMessage
+        this.learnMoreMessage = learnMoreMessage
+
+        // If view already created, update all children
+        if (rootView != null) {
+            updateAll()
+        }
+    }
+
+    private fun updateAll() {
+        updatePurposeMessage()
+        updateSettingsMessage()
+        updateLearnMoreMessage()
+
+        // Animate change in size
+        // Grow or shrink the content container to size of new content
+        val growShrinkToNewContentSize = ChangeBounds()
+        growShrinkToNewContentSize.duration = ANIMATION_DURATION_MILLIS
+        growShrinkToNewContentSize.interpolator = AnimationUtils.loadInterpolator(mActivity,
+            android.R.interpolator.fast_out_slow_in)
+        TransitionManager.beginDelayedTransition(rootView, growShrinkToNewContentSize)
+    }
+
+    override fun createView(): View {
+        // Make this activity be Non-IME target to prevent hiding keyboard flicker when it show up.
+        mActivity.window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
+
+        val rootView = LayoutInflater.from(mActivity)
+            .inflate(R.layout.permission_rationale, null) as ViewGroup
+
+        // Uses the vertical gravity of the PermissionGrantSingleton style to position the window
+        val gravity = rootView.requireViewById<LinearLayout>(R.id.grant_singleton).gravity
+        val verticalGravity = Gravity.VERTICAL_GRAVITY_MASK and gravity
+        mActivity.window.setGravity(Gravity.CENTER_HORIZONTAL or verticalGravity)
+
+        // Cancel dialog
+        rootView.findViewById<View>(R.id.grant_singleton)!!.setOnClickListener(this)
+        // Swallow click event
+        rootView.findViewById<View>(R.id.grant_dialog)!!.setOnClickListener(this)
+
+        purposeMessageView = rootView.findViewById(R.id.purpose_message)
+        purposeMessageView!!.movementMethod = LinkMovementMethod.getInstance()
+
+        settingsMessageView = rootView.findViewById(R.id.settings_message)
+        settingsMessageView!!.movementMethod = LinkMovementMethod.getInstance()
+
+        learnMoreMessageView = rootView.findViewById(R.id.learn_more_message)
+        learnMoreMessageView!!.movementMethod = LinkMovementMethod.getInstance()
+
+        backButton = rootView.findViewById<Button>(R.id.back_button)
+        backButton!!.setOnClickListener(this)
+
+        this.rootView = rootView
+
+        // If ui model present, update all children
+        if (groupName != null) {
+            updateAll()
+        }
+
+        return rootView
+    }
+
+    override fun onClick(view: View) {
+        val id = view.id
+
+        if (id == R.id.grant_singleton) {
+            onCancelled()
+            return
+        }
+
+        if (id == R.id.back_button) {
+            onCancelled()
+        }
+    }
+
+    override fun onBackPressed() {
+        onCancelled()
+    }
+
+    override fun onCancelled() {
+        resultListener.onPermissionRationaleResult(groupName, CANCELLED)
+    }
+
+    private fun updatePurposeMessage() {
+        if (purposeMessage == null) {
+            purposeMessageView!!.visibility = View.GONE
+        } else {
+            purposeMessageView!!.text = purposeMessage
+            purposeMessageView!!.visibility = View.VISIBLE
+        }
+    }
+
+    private fun updateSettingsMessage() {
+        if (settingsMessage == null) {
+            settingsMessageView!!.visibility = View.GONE
+        } else {
+            settingsMessageView!!.text = settingsMessage
+            settingsMessageView!!.visibility = View.VISIBLE
+        }
+    }
+
+    private fun updateLearnMoreMessage() {
+        if (learnMoreMessage == null) {
+            learnMoreMessageView!!.visibility = View.GONE
+        } else {
+            learnMoreMessageView!!.text = learnMoreMessage
+            learnMoreMessageView!!.visibility = View.VISIBLE
+        }
+    }
+
+    companion object {
+        private val TAG = PermissionRationaleViewHandlerImpl::class.java.simpleName
+
+        const val ARG_GROUP_NAME = "ARG_GROUP_NAME"
+        const val ARG_PURPOSE_MESSAGE = "ARG_PURPOSE_MESSAGE"
+        const val ARG_SETTINGS_MESSAGE = "ARG_SETTINGS_MESSAGE"
+        const val ARG_LEARN_MORE_MESSAGE = "ARG_LEARN_MORE_MESSAGE"
+
+        // Animation parameters.
+        private const val ANIMATION_DURATION_MILLIS: Long = 200
+    }
+}
diff --git a/service/java/com/android/permission/access/package-info.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/package-info.java
similarity index 73%
copy from service/java/com/android/permission/access/package-info.java
copy to PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/package-info.java
index af115be..48eb866 100644
--- a/service/java/com/android/permission/access/package-info.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -14,9 +14,5 @@
  * limitations under the License.
  */
 
-/**
- * @hide
- * TODO(b/146466118) remove this javadoc tag
- */
-@android.annotation.Hide
-package com.android.permission.access;
+@androidx.annotation.RequiresApi(android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+package com.android.permissioncontroller.permission.ui.handheld.v34;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt
index bb060e8..5e3cd86 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt
@@ -38,7 +38,6 @@
 import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_GROUPS_FRAGMENT_AUTO_REVOKE_ACTION__ACTION__SWITCH_ENABLED
 import com.android.permissioncontroller.R
 import com.android.permissioncontroller.hibernation.isHibernationEnabled
-import com.android.permissioncontroller.permission.utils.PermissionMapping
 import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData
 import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData
 import com.android.permissioncontroller.permission.data.HibernationSettingStateLiveData
@@ -47,19 +46,20 @@
 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData.Companion.NON_RUNTIME_NORMAL_PERMS
 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
 import com.android.permissioncontroller.permission.data.get
-import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage
 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState
+import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage
 import com.android.permissioncontroller.permission.ui.Category
-import com.android.permissioncontroller.permission.ui.handheld.v31.is7DayToggleEnabled
 import com.android.permissioncontroller.permission.utils.IPC
+import com.android.permissioncontroller.permission.utils.KotlinUtils
+import com.android.permissioncontroller.permission.utils.PermissionMapping
 import com.android.permissioncontroller.permission.utils.Utils
 import com.android.permissioncontroller.permission.utils.Utils.AppPermsLastAccessType
 import com.android.permissioncontroller.permission.utils.navigateSafe
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.launch
 import java.time.Instant
 import java.util.concurrent.TimeUnit
 import kotlin.math.max
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
 
 /**
  * ViewModel for the AppPermissionGroupsFragment. Has a liveData with the UI information for all
@@ -281,7 +281,7 @@
             return
         }
 
-        val aggregateDataFilterBeginDays = if (is7DayToggleEnabled())
+        val aggregateDataFilterBeginDays = if (KotlinUtils.is7DayToggleEnabled())
             AGGREGATE_DATA_FILTER_BEGIN_DAYS_7 else AGGREGATE_DATA_FILTER_BEGIN_DAYS_1
 
         accessTime.clear()
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt
index 7781216..0a56095 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt
@@ -19,21 +19,27 @@
 import android.Manifest
 import android.Manifest.permission.ACCESS_COARSE_LOCATION
 import android.Manifest.permission.ACCESS_FINE_LOCATION
-import android.Manifest.permission_group.LOCATION
+import android.Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
+import android.Manifest.permission_group.READ_MEDIA_VISUAL
 import android.annotation.SuppressLint
 import android.app.AppOpsManager
 import android.app.AppOpsManager.MODE_ALLOWED
 import android.app.AppOpsManager.MODE_ERRORED
 import android.app.AppOpsManager.OPSTR_MANAGE_EXTERNAL_STORAGE
 import android.app.Application
+import android.content.Context
 import android.content.Intent
 import android.os.Build
 import android.os.Bundle
 import android.os.UserHandle
+import android.provider.MediaStore
 import android.util.Log
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContract
 import androidx.annotation.ChecksSdkIntAtLeast
 import androidx.annotation.RequiresApi
 import androidx.annotation.StringRes
+import androidx.core.util.Consumer
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
@@ -44,7 +50,6 @@
 import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED
 import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_VIEWED
 import com.android.permissioncontroller.R
-import com.android.permissioncontroller.permission.utils.PermissionMapping
 import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData
 import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData.FullStoragePackageState
 import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
@@ -55,9 +60,6 @@
 import com.android.permissioncontroller.permission.service.PermissionChangeStorageImpl
 import com.android.permissioncontroller.permission.service.v33.PermissionDecisionStorageImpl
 import com.android.permissioncontroller.permission.ui.AdvancedConfirmDialogArgs
-
-import com.android.permissioncontroller.permission.ui.handheld.v31.getDefaultPrecision
-import com.android.permissioncontroller.permission.ui.handheld.v31.isLocationAccuracyEnabled
 import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.ALLOW
 import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.ALLOW_ALWAYS
 import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.ALLOW_FOREGROUND
@@ -66,8 +68,12 @@
 import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.DENY
 import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.DENY_FOREGROUND
 import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.LOCATION_ACCURACY
+import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType.SELECT_PHOTOS
 import com.android.permissioncontroller.permission.utils.KotlinUtils
+import com.android.permissioncontroller.permission.utils.KotlinUtils.getDefaultPrecision
+import com.android.permissioncontroller.permission.utils.KotlinUtils.isLocationAccuracyEnabled
 import com.android.permissioncontroller.permission.utils.LocationUtils
+import com.android.permissioncontroller.permission.utils.PermissionMapping
 import com.android.permissioncontroller.permission.utils.SafetyNetLogger
 import com.android.permissioncontroller.permission.utils.Utils
 import com.android.permissioncontroller.permission.utils.navigateSafe
@@ -98,6 +104,7 @@
         private val LOG_TAG = AppPermissionViewModel::class.java.simpleName
 
         private const val DEVICE_PROFILE_ROLE_PREFIX = "android.app.role"
+        const val PHOTO_PICKER_REQUEST_CODE = 1
     }
 
     interface ConfirmDialogShowingFragment {
@@ -112,21 +119,22 @@
     }
 
     enum class ChangeRequest(val value: Int) {
-        GRANT_FOREGROUND(1),
-        REVOKE_FOREGROUND(2),
-        GRANT_BACKGROUND(4),
-        REVOKE_BACKGROUND(8),
+        GRANT_FOREGROUND(1 shl 0),
+        REVOKE_FOREGROUND(1 shl 1),
+        GRANT_BACKGROUND(1 shl 2),
+        REVOKE_BACKGROUND(1 shl 3),
         GRANT_BOTH(GRANT_FOREGROUND.value or GRANT_BACKGROUND.value),
         REVOKE_BOTH(REVOKE_FOREGROUND.value or REVOKE_BACKGROUND.value),
         GRANT_FOREGROUND_ONLY(GRANT_FOREGROUND.value or REVOKE_BACKGROUND.value),
-        GRANT_All_FILE_ACCESS(16),
-        GRANT_FINE_LOCATION(32),
-        REVOKE_FINE_LOCATION(64),
-        GRANT_STORAGE_SUPERGROUP(128),
-        REVOKE_STORAGE_SUPERGROUP(256),
+        GRANT_All_FILE_ACCESS(1 shl 4),
+        GRANT_FINE_LOCATION(1 shl 5),
+        REVOKE_FINE_LOCATION(1 shl 6),
+        GRANT_STORAGE_SUPERGROUP(1 shl 7),
+        REVOKE_STORAGE_SUPERGROUP(1 shl 8),
         GRANT_STORAGE_SUPERGROUP_CONFIRMED(
                 GRANT_STORAGE_SUPERGROUP.value or GRANT_FOREGROUND.value),
-        REVOKE_STORAGE_SUPERGROUP_CONFIRMED(REVOKE_STORAGE_SUPERGROUP.value or REVOKE_BOTH.value);
+        REVOKE_STORAGE_SUPERGROUP_CONFIRMED(REVOKE_STORAGE_SUPERGROUP.value or REVOKE_BOTH.value),
+        PHOTOS_SELECTED( 1 shl 9);
 
         infix fun andValue(other: ChangeRequest): Int {
             return value and other.value
@@ -141,13 +149,16 @@
         ASK(4),
         DENY(5),
         DENY_FOREGROUND(6),
-        LOCATION_ACCURACY(7);
+        LOCATION_ACCURACY(7),
+        SELECT_PHOTOS( 8);
     }
 
     private val isStorageAndLessThanT =
         permGroupName == Manifest.permission_group.STORAGE && !SdkLevel.isAtLeastT()
     private var hasConfirmedRevoke = false
     private var lightAppPermGroup: LightAppPermGroup? = null
+    private var photoPickerLauncher: ActivityResultLauncher<Unit>? = null
+    private var photoPickerResultConsumer: Consumer<Int>? = null
 
     private val mediaStorageSupergroupPermGroups = mutableMapOf<String, LightAppPermGroup>()
 
@@ -200,8 +211,8 @@
     /**
      * A livedata which computes the state of the radio buttons
      */
-    val buttonStateLiveData = object
-        : SmartUpdateMediatorLiveData<@JvmSuppressWildcards Map<ButtonType, ButtonState>>() {
+    val buttonStateLiveData = object :
+        SmartUpdateMediatorLiveData<@JvmSuppressWildcards Map<ButtonType, ButtonState>>() {
 
         private val appPermGroupLiveData = LightAppPermGroupLiveData[packageName, permGroupName,
             user]
@@ -275,6 +286,7 @@
             val askState = ButtonState()
             val deniedState = ButtonState()
             val deniedForegroundState = ButtonState()
+            val selectState = ButtonState()
 
             askOneTimeState.isShown = group.foreground.isGranted && group.isOneTime
             askState.isShown = PermissionMapping.supportsOneTimeGrant(permGroupName) &&
@@ -315,6 +327,19 @@
                     val detailId = getIndividualPermissionDetailResId(group)
                     detailResIdLiveData.value = detailId.first to detailId.second
                 }
+            } else if (KotlinUtils.isPhotoPickerPromptEnabled() &&
+                group.permGroupName == READ_MEDIA_VISUAL &&
+                group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU) {
+                // Allow / Select Photos / Deny case
+                allowedState.isShown = true
+                deniedState.isShown = true
+                selectState.isShown = true
+
+                deniedState.isChecked = !group.isGranted
+                val  && group.permissions.values.all {
+                    it.name == READ_MEDIA_VISUAL_USER_SELECTED || !it.isGrantedIncludingAppOp }
+                selectState.isChecked = onlyUserSelectedGranted
+                allowedState.isChecked = group.isGranted && !onlyUserSelectedGranted
             } else {
                 // Allow / Deny case
                 allowedState.isShown = true
@@ -376,9 +401,8 @@
             }
 
             if (shouldShowLocationAccuracy == null) {
-                shouldShowLocationAccuracy = group.permGroupName == LOCATION &&
-                        group.permissions.containsKey(ACCESS_FINE_LOCATION) &&
-                        isLocationAccuracyEnabled()
+                shouldShowLocationAccuracy = isLocationAccuracyEnabled() &&
+                        group.permissions.containsKey(ACCESS_FINE_LOCATION)
             }
             val locationAccuracyState = ButtonState(isFineLocationChecked(group),
                     true, false, null)
@@ -393,10 +417,36 @@
                 ALLOW to allowedState, ALLOW_ALWAYS to allowedAlwaysState,
                 ALLOW_FOREGROUND to allowedForegroundState, ASK_ONCE to askOneTimeState,
                 ASK to askState, DENY to deniedState, DENY_FOREGROUND to deniedForegroundState,
-                LOCATION_ACCURACY to locationAccuracyState)
+                LOCATION_ACCURACY to locationAccuracyState, SELECT_PHOTOS to selectState)
         }
     }
 
+    fun registerPhotoPickerResultIfNeeded(fragment: Fragment) {
+        if (permGroupName != READ_MEDIA_VISUAL) {
+            return
+        }
+        photoPickerLauncher = fragment.registerForActivityResult(
+            object : ActivityResultContract<Unit, Int>() {
+            override fun parseResult(resultCode: Int, intent: Intent?): Int {
+                return resultCode
+            }
+
+            override fun createIntent(context: Context, input: Unit): Intent {
+                return Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP)
+                    .putExtra(Intent.EXTRA_UID, lightAppPermGroup?.packageInfo?.uid)
+                    .setType(KotlinUtils.getMimeTypeForPermissions(
+                        lightAppPermGroup?.foregroundPermNames ?: emptyList()))
+            }
+        }) { result ->
+            photoPickerResultConsumer?.accept(result)
+        }
+    }
+
+    fun openPhotoPicker(onResult: Consumer<Int>) {
+        photoPickerResultConsumer = onResult
+        photoPickerLauncher?.launch(Unit)
+    }
+
     private fun isFineLocationChecked(group: LightAppPermGroup): Boolean {
         if (shouldShowLocationAccuracy == true) {
             val coarseLocation = group.permissions[ACCESS_COARSE_LOCATION]!!
@@ -568,6 +618,17 @@
             return
         }
 
+        if (changeRequest == ChangeRequest.PHOTOS_SELECTED) {
+            val nonSelectedPerms = group.permissions.keys.filter {
+                it != READ_MEDIA_VISUAL_USER_SELECTED }
+            var newGroup = KotlinUtils.revokeForegroundRuntimePermissions(app, group,
+                filterPermissions = nonSelectedPerms)
+            newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, newGroup,
+            filterPermissions = listOf(READ_MEDIA_VISUAL_USER_SELECTED))
+            logPermissionChanges(group, newGroup, buttonClicked)
+            return
+        }
+
         val shouldGrantForeground = changeRequest andValue ChangeRequest.GRANT_FOREGROUND != 0
         val shouldGrantBackground = changeRequest andValue ChangeRequest.GRANT_BACKGROUND != 0
         val shouldRevokeForeground = changeRequest andValue ChangeRequest.REVOKE_FOREGROUND != 0
@@ -669,11 +730,12 @@
             }
 
             if (shouldGrantForeground) {
-                if (shouldShowLocationAccuracy == true && !isFineLocationChecked(newGroup)) {
-                    newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, newGroup,
-                            filterPermissions = listOf(ACCESS_COARSE_LOCATION))
+                newGroup = if (shouldShowLocationAccuracy == true &&
+                    !isFineLocationChecked(newGroup)) {
+                    KotlinUtils.grantForegroundRuntimePermissions(app, newGroup,
+                        filterPermissions = listOf(ACCESS_COARSE_LOCATION))
                 } else {
-                    newGroup = KotlinUtils.grantForegroundRuntimePermissions(app, newGroup)
+                    KotlinUtils.grantForegroundRuntimePermissions(app, newGroup)
                 }
 
                 if (!wasForegroundGranted) {
@@ -713,6 +775,23 @@
         }
     }
 
+    private fun getPermGroupIcon(permGroup: String) =
+            Utils.getGroupInfo(permGroup, app.applicationContext)?.icon ?: R.drawable.ic_empty_icon
+
+    private val storagePermGroupIcon = getPermGroupIcon(Manifest.permission_group.STORAGE)
+
+    private val auralPermGroupIcon = if (SdkLevel.isAtLeastT()) {
+        getPermGroupIcon(Manifest.permission_group.READ_MEDIA_AURAL)
+    } else {
+        R.drawable.ic_empty_icon
+    }
+
+    private val visualPermGroupIcon = if (SdkLevel.isAtLeastT()) {
+        getPermGroupIcon(Manifest.permission_group.READ_MEDIA_VISUAL)
+    } else {
+        R.drawable.ic_empty_icon
+    }
+
     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
     private fun showMediaConfirmDialog(
         setOneTime: Boolean,
@@ -731,42 +810,42 @@
         val (iconId, titleId, messageId) = when {
             targetSdk < Build.VERSION_CODES.Q && aural && allow ->
                 Triple(
-                    R.drawable.perm_group_storage,
+                    storagePermGroupIcon,
                     R.string.media_confirm_dialog_title_a_to_p_aural_allow,
                     R.string.media_confirm_dialog_message_a_to_p_aural_allow)
             targetSdk < Build.VERSION_CODES.Q && aural && deny ->
                 Triple(
-                    R.drawable.perm_group_storage,
+                    storagePermGroupIcon,
                     R.string.media_confirm_dialog_title_a_to_p_aural_deny,
                     R.string.media_confirm_dialog_message_a_to_p_aural_deny)
             targetSdk < Build.VERSION_CODES.Q && visual && allow ->
                 Triple(
-                    R.drawable.perm_group_storage,
+                    storagePermGroupIcon,
                     R.string.media_confirm_dialog_title_a_to_p_visual_allow,
                     R.string.media_confirm_dialog_message_a_to_p_visual_allow)
             targetSdk < Build.VERSION_CODES.Q && visual && deny ->
                 Triple(
-                    R.drawable.perm_group_storage,
+                    storagePermGroupIcon,
                     R.string.media_confirm_dialog_title_a_to_p_visual_deny,
                     R.string.media_confirm_dialog_message_a_to_p_visual_deny)
             targetSdk <= Build.VERSION_CODES.S_V2 && aural && allow ->
                 Triple(
-                    R.drawable.perm_group_visual,
+                    visualPermGroupIcon,
                     R.string.media_confirm_dialog_title_q_to_s_aural_allow,
                     R.string.media_confirm_dialog_message_q_to_s_aural_allow)
             targetSdk <= Build.VERSION_CODES.S_V2 && aural && deny ->
                 Triple(
-                    R.drawable.perm_group_visual,
+                    visualPermGroupIcon,
                     R.string.media_confirm_dialog_title_q_to_s_aural_deny,
                     R.string.media_confirm_dialog_message_q_to_s_aural_deny)
             targetSdk <= Build.VERSION_CODES.S_V2 && visual && allow ->
                 Triple(
-                    R.drawable.perm_group_aural,
+                    auralPermGroupIcon,
                     R.string.media_confirm_dialog_title_q_to_s_visual_allow,
                     R.string.media_confirm_dialog_message_q_to_s_visual_allow)
             targetSdk <= Build.VERSION_CODES.S_V2 && visual && deny ->
                 Triple(
-                    R.drawable.perm_group_aural,
+                    auralPermGroupIcon,
                     R.string.media_confirm_dialog_title_q_to_s_visual_deny,
                     R.string.media_confirm_dialog_message_q_to_s_visual_deny)
             else ->
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
index b7566e7..a474bab 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
@@ -14,13 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.permission.ui.model.v31
+package com.android.permissioncontroller.permission.ui.model
 
 import android.Manifest
 import android.Manifest.permission.ACCESS_COARSE_LOCATION
 import android.Manifest.permission.ACCESS_FINE_LOCATION
 import android.Manifest.permission.POST_NOTIFICATIONS
+import android.Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
 import android.Manifest.permission_group.LOCATION
+import android.Manifest.permission_group.READ_MEDIA_VISUAL
 import android.annotation.SuppressLint
 import android.app.Activity
 import android.app.Application
@@ -30,15 +32,24 @@
 import android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED
 import android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED
 import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET
+import android.healthconnect.HealthConnectManager.ACTION_MANAGE_HEALTH_PERMISSIONS
+import android.healthconnect.HealthConnectManager.isHealthPermission
+import android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP
 import android.os.Build
 import android.os.Bundle
 import android.os.Process
 import android.permission.PermissionManager
+import android.provider.MediaStore
 import android.util.Log
+import androidx.annotation.ChecksSdkIntAtLeast
 import androidx.core.util.Consumer
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
 import com.android.modules.utils.build.SdkLevel
+import com.android.permission.safetylabel.DataCategory
+import com.android.permission.safetylabel.DataType
+import com.android.permission.safetylabel.DataTypeConstants
+import com.android.permission.safetylabel.SafetyLabel
 import com.android.permissioncontroller.Constants
 import com.android.permissioncontroller.DeviceUtils
 import com.android.permissioncontroller.PermissionControllerApplication
@@ -50,6 +61,7 @@
 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED
 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_RESTRICTED_PERMISSION
 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED
+import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__PHOTOS_SELECTED
 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED
 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS
 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE
@@ -59,10 +71,10 @@
 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_ONE_TIME
 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED
 import com.android.permissioncontroller.auto.DrivingDecisionReminderService
-import com.android.permissioncontroller.permission.utils.PermissionMapping
 import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
 import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData
 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData
+import com.android.permissioncontroller.permission.data.SafetyLabelInfoLiveData
 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
 import com.android.permissioncontroller.permission.data.get
 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
@@ -72,16 +84,21 @@
 import com.android.permissioncontroller.permission.service.v33.PermissionDecisionStorageImpl
 import com.android.permissioncontroller.permission.ui.AutoGrantPermissionsNotifier
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALL_PHOTOS_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_FOREGROUND_BUTTON
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_MORE_SELECTED_PHOTOS_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ONE_TIME_BUTTON
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_SELECTED_PHOTOS_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.COARSE_RADIO_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_AND_DONT_ASK_AGAIN_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_BOTH_LOCATIONS
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_COARSE_LOCATION_ONLY
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_FINE_LOCATION_ONLY
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DONT_ALLOW_MORE_SELECTED_PHOTOS_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.FINE_RADIO_BUTTON
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.INTENT_PHOTOS_SELECTED
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LINK_TO_SETTINGS
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LOCATION_ACCURACY_LAYOUT
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_BUTTON
@@ -90,19 +107,27 @@
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON
 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_BUTTON
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.PERMISSION_TO_BIT_SHIFT
-import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler
+import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.CANCELED
 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED
 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN
+import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_MORE_PHOTOS
 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS
 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY
+import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ONE_TIME
+import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_USER_SELECTED
 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity
 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED
 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT
-import com.android.permissioncontroller.permission.ui.handheld.v31.getDefaultPrecision
-import com.android.permissioncontroller.permission.ui.handheld.v31.isLocationAccuracyEnabled
+import com.android.permissioncontroller.permission.ui.v34.PermissionRationaleActivity
 import com.android.permissioncontroller.permission.utils.AdminRestrictedPermissionsUtils
 import com.android.permissioncontroller.permission.utils.KotlinUtils
+import com.android.permissioncontroller.permission.utils.KotlinUtils.getDefaultPrecision
+import com.android.permissioncontroller.permission.utils.KotlinUtils.grantBackgroundRuntimePermissions
+import com.android.permissioncontroller.permission.utils.KotlinUtils.grantForegroundRuntimePermissions
+import com.android.permissioncontroller.permission.utils.KotlinUtils.isLocationAccuracyEnabled
+import com.android.permissioncontroller.permission.utils.KotlinUtils.isPermissionRationaleEnabled
+import com.android.permissioncontroller.permission.utils.PermissionMapping
+import com.android.permissioncontroller.permission.utils.SafetyLabelPermissionMapping
 import com.android.permissioncontroller.permission.utils.SafetyNetLogger
 import com.android.permissioncontroller.permission.utils.Utils
 
@@ -127,11 +152,12 @@
     private val LOG_TAG = GrantPermissionsViewModel::class.java.simpleName
     private val user = Process.myUserHandle()
     private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user]
+    private val safetyLabelInfoLiveData = SafetyLabelInfoLiveData[packageName, user]
     private val dpm = app.getSystemService(DevicePolicyManager::class.java)!!
     private val permissionPolicy = dpm.getPermissionPolicy(null)
     private val permGroupsToSkip = mutableListOf<String>()
     private var groupStates = mutableMapOf<Pair<String, Boolean>, GroupState>()
-    private var isFirstTimeRequestingFineAndCoarse: Boolean = false
+    private val permissionRationaleEnabled: Boolean by lazy { isPermissionRationaleEnabled() }
 
     private var autoGrantNotifier: AutoGrantPermissionsNotifier? = null
     private fun getAutoGrantNotifier(): AutoGrantPermissionsNotifier {
@@ -140,6 +166,7 @@
     }
 
     private lateinit var packageInfo: LightPackageInfo
+    private var safetyLabel: SafetyLabel? = null
 
     // All permissions that could possibly be affected by the provided requested permissions, before
     // filtering system fixed, auto grant, etc.
@@ -159,7 +186,9 @@
         val locationVisibilities: List<Boolean> = List(NEXT_LOCATION_DIALOG) { false },
         val message: RequestMessage = RequestMessage.FG_MESSAGE,
         val detailMessage: RequestMessage = RequestMessage.NO_MESSAGE,
-        val sendToSettingsImmediately: Boolean = false
+        val sendToSettingsImmediately: Boolean = false,
+        val openPhotoPicker: Boolean = false,
+        val showPermissionRationale: Boolean = false
     ) {
         val groupName = groupInfo.name
     }
@@ -174,18 +203,33 @@
         private val LOG_TAG = GrantPermissionsViewModel::class.java.simpleName
         private val packagePermissionsLiveData = PackagePermissionsLiveData[packageName, user]
 
+        // TODO(b/260873483): only query safety label for supported permission groups. should only
+        //  query location, but currently queries for all groups
         init {
             addSource(packagePermissionsLiveData) { onPackageLoaded() }
             addSource(packageInfoLiveData) { onPackageLoaded() }
+            if (permissionRationaleEnabled) {
+                addSource(safetyLabelInfoLiveData) { onPackageLoaded() }
+            }
+
             // Load package state, if available
             onPackageLoaded()
         }
 
         private fun onPackageLoaded() {
-            if (packageInfoLiveData.isStale || packagePermissionsLiveData.isStale) {
+            if (packageInfoLiveData.isStale ||
+                packagePermissionsLiveData.isStale ||
+                (permissionRationaleEnabled && safetyLabelInfoLiveData.isStale)) {
                 return
             }
 
+            safetyLabel =
+                if (permissionRationaleEnabled) {
+                    safetyLabelInfoLiveData.value?.safetyLabel
+                } else {
+                    null
+                }
+
             val groups = packagePermissionsLiveData.value
             val pI = packageInfoLiveData.value
             if (groups == null || groups.isEmpty() || pI == null) {
@@ -209,7 +253,7 @@
             }
             unfilteredAffectedPermissions = allAffectedPermissions.toList()
 
-            getAppPermGroups(groups.toMutableMap().apply {
+            setAppPermGroupsLiveDatas(groups.toMutableMap().apply {
                 remove(PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS)
             })
 
@@ -219,7 +263,7 @@
             }
         }
 
-        private fun getAppPermGroups(groups: Map<String, List<String>>) {
+        private fun setAppPermGroupsLiveDatas(groups: Map<String, List<String>>) {
 
             val requestedGroups = groups.filter { (_, perms) ->
                 perms.any { it in unfilteredAffectedPermissions }
@@ -276,10 +320,10 @@
                 groupStates = getRequiredGroupStates(
                     appPermGroupLiveDatas.mapNotNull { it.value.value })
             }
-            getRequestInfosFromGroupStates()
+            setRequestInfosFromGroupStates()
         }
 
-        private fun getRequestInfosFromGroupStates() {
+        private fun setRequestInfosFromGroupStates() {
             val requestInfos = mutableListOf<RequestInfo>()
             for ((key, groupState) in groupStates) {
                 val groupInfo = groupState.group.permGroupInfo
@@ -325,7 +369,29 @@
                 // Whether or not to use the foreground, background, or no detail message.
                 // null ==
                 var detailMessage = RequestMessage.NO_MESSAGE
-                if (groupState.group.packageInfo.targetSdkVersion >=
+
+                if (shouldShowMorePhotosMessage(groupState.group)) {
+                    buttonVisibilities[ALLOW_BUTTON] = false
+                    buttonVisibilities[DENY_BUTTON] = false
+                    buttonVisibilities[ALLOW_MORE_SELECTED_PHOTOS_BUTTON] = true
+                    buttonVisibilities[DONT_ALLOW_MORE_SELECTED_PHOTOS_BUTTON] = true
+                    message = RequestMessage.MORE_PHOTOS_MESSAGE
+                } else if (KotlinUtils.isPhotoPickerPromptEnabled() &&
+                    groupState.group.permGroupName == READ_MEDIA_VISUAL &&
+                    groupState.group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU) {
+                    // If the USER_SELECTED permission is user fixed and granted, or the app is only
+                    // requesting USER_SELECTED, direct straight to photo picker
+                    val userPerm = groupState.group.permissions[READ_MEDIA_VISUAL_USER_SELECTED]
+                    if ((userPerm?.isUserFixed == true && userPerm.isGrantedIncludingAppOp) ||
+                        groupState.affectedPermissions == listOf(READ_MEDIA_VISUAL_USER_SELECTED)) {
+                        requestInfos.add(RequestInfo(groupInfo, openPhotoPicker = true))
+                        continue
+                    } else {
+                        buttonVisibilities[ALLOW_BUTTON] = false
+                        buttonVisibilities[ALLOW_SELECTED_PHOTOS_BUTTON] = true
+                        buttonVisibilities[ALLOW_ALL_PHOTOS_BUTTON] = true
+                    }
+                } else if (groupState.group.packageInfo.targetSdkVersion >=
                         minSdkForOrderedSplitPermissions) {
                     if (isBackground || Utils.hasPermWithBackgroundModeCompat(groupState.group)) {
                         if (needFgPermissions) {
@@ -442,7 +508,7 @@
                 // Show location permission dialogs based on location permissions
                 val locationVisibilities = MutableList(NEXT_LOCATION_DIALOG) { false }
                 if (groupState.group.permGroupName == LOCATION && isLocationAccuracyEnabled() &&
-                        packageInfo.targetSdkVersion >= Build.VERSION_CODES.S) {
+                    packageInfo.targetSdkVersion >= Build.VERSION_CODES.S) {
                     if (needFgPermissions) {
                         locationVisibilities[LOCATION_ACCURACY_LAYOUT] = true
                         if (fgState != null &&
@@ -464,11 +530,6 @@
                                     value = null
                                     return
                                 }
-                                if (coarseLocationPerm?.isOneTime == false &&
-                                        !coarseLocationPerm.isUserSet &&
-                                        !coarseLocationPerm.isUserFixed) {
-                                    isFirstTimeRequestingFineAndCoarse = true
-                                }
                                 // Normal flow with both Coarse and Fine locations
                                 locationVisibilities[DIALOG_WITH_BOTH_LOCATIONS] = true
                                 // Steps to decide location accuracy default state
@@ -521,19 +582,12 @@
                     buttonVisibilities,
                     locationVisibilities,
                     message,
-                    detailMessage))
+                    detailMessage,
+                    showPermissionRationale = shouldShowPermissionRationale(
+                        safetyLabel, groupState)))
             }
-            requestInfos.sortWith(Comparator { rhs, lhs ->
-                val rhsHasOneTime = rhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON]
-                val lhsHasOneTime = lhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON]
-                if (rhsHasOneTime && !lhsHasOneTime) {
-                    -1
-                } else if (!rhsHasOneTime && lhsHasOneTime) {
-                    1
-                } else {
-                    rhs.groupName.compareTo(lhs.groupName)
-                }
-            })
+
+            sortPermissionGroups(requestInfos)
 
             value = if (requestInfos.any { it.sendToSettingsImmediately } &&
                 requestInfos.size > 1) {
@@ -546,6 +600,55 @@
         }
     }
 
+    fun sortPermissionGroups(requestInfos: MutableList<RequestInfo>) {
+        requestInfos.sortWith { rhs, lhs ->
+            val rhsHasOneTime = rhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON]
+            val lhsHasOneTime = lhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON]
+            if (rhsHasOneTime && !lhsHasOneTime) {
+                -1
+            } else if ((!rhsHasOneTime && lhsHasOneTime) ||
+                isHealthPermissionGroup(rhs.groupName)
+            ) {
+                1
+            } else {
+                rhs.groupName.compareTo(lhs.groupName)
+            }
+        }
+    }
+
+    private fun shouldShowPermissionRationale(
+        safetyLabel: SafetyLabel?,
+        groupState: GroupState
+    ): Boolean {
+        if (safetyLabel == null || safetyLabel.dataLabel.dataShared.isEmpty()) {
+            return false
+        }
+
+        val groupName = groupState.group.permGroupName
+        val categoriesForPermission: List<String> =
+            SafetyLabelPermissionMapping.getCategoriesForPermissionGroup(groupName)
+        categoriesForPermission.forEach categoryLoop@ { category ->
+            val dataCategory: DataCategory? = safetyLabel.dataLabel.dataShared[category]
+            if (dataCategory == null) {
+                // Continue to next
+                return@categoryLoop
+            }
+            val typesForCategory = DataTypeConstants.getValidDataTypesForCategory(category)
+            typesForCategory.forEach typeLoop@ { type ->
+                val dataType: DataType? = dataCategory.dataTypes[type]
+                if (dataType == null) {
+                    // Continue to next
+                    return@typeLoop
+                }
+                if (dataType.purposeSet.isNotEmpty()) {
+                    return true
+                }
+            }
+        }
+
+        return false
+    }
+
     /**
      * Converts a list of LightAppPermGroups into a list of GroupStates
      */
@@ -652,6 +755,10 @@
             return false
         }
 
+        if (HEALTH_PERMISSION_GROUP == group.permGroupName) {
+            return !(group.permissions[perm]?.isUserFixed ?: true)
+        }
+
         val subGroup = if (perm in group.backgroundPermNames) {
             group.background
         } else {
@@ -673,6 +780,9 @@
                     // is still grantable.
                     return true
                 }
+            } else if (perm == READ_MEDIA_VISUAL_USER_SELECTED) {
+                // If USER_SELECTED is granted as fixed, we should immediately show the photo picker
+                return true
             }
             reportRequestResult(perm,
                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED)
@@ -723,20 +833,13 @@
         }
 
         if ((isBackground && group.background.isGrantedExcludingRWROrAllRWR ||
-            !isBackground && group.foreground.isGrantedExcludingRWROrAllRWR)) {
-            // If FINE location is not granted, do not grant it automatically when COARSE
-            // location is already granted.
-            if (group.permGroupName == LOCATION &&
-                    group.allPermissions[ACCESS_FINE_LOCATION]?.isGrantedIncludingAppOp
-                    == false) {
-                return STATE_UNKNOWN
-            }
-
+            !isBackground && group.foreground.isGrantedExcludingRWROrAllRWR) &&
+            canAutoGrantWholeGroup(group)) {
             if (group.permissions[perm]?.isGrantedIncludingAppOp == false) {
                 if (isBackground) {
-                    KotlinUtils.grantBackgroundRuntimePermissions(app, group, listOf(perm))
+                    grantBackgroundRuntimePermissions(app, group, listOf(perm))
                 } else {
-                    KotlinUtils.grantForegroundRuntimePermissions(app, group, listOf(perm))
+                    grantForegroundRuntimePermissions(app, group, listOf(perm), group.isOneTime)
                 }
                 KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_USER_SET to false,
                     FLAG_PERMISSION_USER_FIXED to false, filterPermissions = listOf(perm))
@@ -753,6 +856,33 @@
         return STATE_UNKNOWN
     }
 
+    /**
+     * Determines if remaining permissions in the group can be auto granted based on
+     * granted permissions in the group.
+     */
+    private fun canAutoGrantWholeGroup(group: LightAppPermGroup): Boolean {
+        // If FINE location is not granted, do not grant it automatically when COARSE
+        // location is already granted.
+        if (group.permGroupName == LOCATION &&
+            group.allPermissions[ACCESS_FINE_LOCATION]?.isGrantedIncludingAppOp == false) {
+            return false
+        }
+        // If READ_MEDIA_VISUAL_USER_SELECTED is the only permission in the group that is granted,
+        // do not grant.
+        if (isVisualUserSelectedOnlyGranted(group) ||
+            HEALTH_PERMISSION_GROUP == group.permGroupName) {
+            return false
+        }
+        return true
+    }
+
+    private fun isVisualUserSelectedOnlyGranted(group: LightAppPermGroup): Boolean {
+       return KotlinUtils.isPhotoPickerPromptEnabled() &&
+               group.permGroupName == READ_MEDIA_VISUAL && group.permissions.values.all {
+           (it.name == READ_MEDIA_VISUAL_USER_SELECTED) || !it.isGrantedIncludingAppOp } &&
+           group.permissions[READ_MEDIA_VISUAL_USER_SELECTED]?.isGrantedIncludingAppOp == true
+    }
+
     private fun getStateFromPolicy(perm: String, group: LightAppPermGroup): Int {
         val isBackground = perm in group.backgroundPermNames
         var skipGroup = false
@@ -762,9 +892,9 @@
                 if (AdminRestrictedPermissionsUtils.mayAdminGrantPermission(
                                 app, perm, user.identifier)) {
                     if (isBackground) {
-                        KotlinUtils.grantBackgroundRuntimePermissions(app, group, listOf(perm))
+                        grantBackgroundRuntimePermissions(app, group, listOf(perm))
                     } else {
-                        KotlinUtils.grantForegroundRuntimePermissions(app, group, listOf(perm))
+                        grantForegroundRuntimePermissions(app, group, listOf(perm))
                     }
                     KotlinUtils.setGroupFlags(app, group, FLAG_PERMISSION_POLICY_FIXED to true,
                             FLAG_PERMISSION_USER_SET to false, FLAG_PERMISSION_USER_FIXED to false,
@@ -837,15 +967,18 @@
         val foregroundGroupState = groupStates[groupName to false]
         val backgroundGroupState = groupStates[groupName to true]
         when (result) {
-            GrantPermissionsViewHandler.CANCELED -> {
+            CANCELED -> {
                 if (foregroundGroupState != null) {
                     reportRequestResult(foregroundGroupState.affectedPermissions,
                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED)
+                    foregroundGroupState.state = STATE_SKIPPED
                 }
                 if (backgroundGroupState != null) {
                     reportRequestResult(backgroundGroupState.affectedPermissions,
                         PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED)
+                    backgroundGroupState.state = STATE_SKIPPED
                 }
+                requestInfosLiveData.update()
                 return
             }
             GRANTED_ALWAYS -> {
@@ -872,7 +1005,7 @@
                         doNotAskAgain = false)
                 }
             }
-            GrantPermissionsViewHandler.GRANTED_ONE_TIME -> {
+            GRANTED_ONE_TIME -> {
                 if (foregroundGroupState != null) {
                     onPermissionGrantResultSingleState(foregroundGroupState,
                         affectedForegroundPermissions, granted = true, isOneTime = true,
@@ -884,6 +1017,11 @@
                         doNotAskAgain = false)
                 }
             }
+            GRANTED_USER_SELECTED, DENIED_MORE_PHOTOS -> {
+                if (foregroundGroupState != null) {
+                    grantUserSelectedVisualGroupPermissions(foregroundGroupState)
+                }
+            }
             DENIED -> {
                 if (foregroundGroupState != null) {
                     onPermissionGrantResultSingleState(foregroundGroupState,
@@ -911,6 +1049,32 @@
         }
     }
 
+    private fun grantUserSelectedVisualGroupPermissions(groupState: GroupState) {
+        val userSelectedPerm =
+            groupState.group.permissions[READ_MEDIA_VISUAL_USER_SELECTED] ?: return
+        val nonSelectedPerms = groupState.affectedPermissions
+            .filter { it != READ_MEDIA_VISUAL_USER_SELECTED }
+        if (userSelectedPerm.isImplicit) {
+            // If the permission is implicit, grant USER_SELECTED as user set, and all other
+            // permissions as one time, and without app ops.
+            KotlinUtils.grantForegroundRuntimePermissions(app, groupState.group,
+                nonSelectedPerms, isOneTime = true, userFixed = false, withoutAppOps = true)
+            KotlinUtils.grantForegroundRuntimePermissions(app, groupState.group,
+                listOf(READ_MEDIA_VISUAL_USER_SELECTED))
+            onPermissionGrantResultSingleState(groupState, listOf(READ_MEDIA_VISUAL_USER_SELECTED),
+                granted = true, isOneTime = false, doNotAskAgain = false)
+        } else {
+            val setUserFixed = userSelectedPerm.isUserFixed || userSelectedPerm.isUserSet
+            KotlinUtils.grantForegroundRuntimePermissions(app, groupState.group,
+                listOf(READ_MEDIA_VISUAL_USER_SELECTED), userFixed = setUserFixed)
+            KotlinUtils.revokeForegroundRuntimePermissions(app, groupState.group,
+                userFixed = setUserFixed,  filterPermissions = nonSelectedPerms)
+        }
+        groupState.state = STATE_ALLOWED
+        reportButtonClickResult(groupState, true,
+            PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__PHOTOS_SELECTED)
+    }
+
     @SuppressLint("NewApi")
     private fun onPermissionGrantResultSingleState(
         groupState: GroupState,
@@ -931,11 +1095,11 @@
                 PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED
             }
             if (groupState.isBackground) {
-                KotlinUtils.grantBackgroundRuntimePermissions(app, groupState.group,
+                grantBackgroundRuntimePermissions(app, groupState.group,
                     groupState.affectedPermissions)
             } else {
                 if (affectedForegroundPermissions == null) {
-                    KotlinUtils.grantForegroundRuntimePermissions(app, groupState.group,
+                    grantForegroundRuntimePermissions(app, groupState.group,
                         groupState.affectedPermissions, isOneTime)
                     // This prevents weird flag state when app targetSDK switches from S+ to R-
                     if (groupState.affectedPermissions.contains(ACCESS_FINE_LOCATION)) {
@@ -943,7 +1107,7 @@
                                 app, groupState.group, true)
                     }
                 } else {
-                    val newGroup = KotlinUtils.grantForegroundRuntimePermissions(app,
+                    val newGroup = grantForegroundRuntimePermissions(app,
                             groupState.group, affectedForegroundPermissions, isOneTime)
                     if (!isOneTime || newGroup.isOneTime) {
                         KotlinUtils.setFlagsWhenLocationAccuracyChanged(app, newGroup,
@@ -975,6 +1139,10 @@
             }
             groupState.state = STATE_DENIED
         }
+        reportButtonClickResult(groupState, granted, result)
+    }
+
+    private fun reportButtonClickResult(groupState: GroupState, granted: Boolean, result: Int) {
         reportRequestResult(groupState.affectedPermissions, result)
         // group state has changed, reload liveData
         requestInfosLiveData.update()
@@ -1087,6 +1255,29 @@
         }
     }
 
+    @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    private fun isHealthPermissionGroup(permGroupName: String): Boolean {
+        return SdkLevel.isAtLeastU() && HEALTH_PERMISSION_GROUP.equals(permGroupName)
+    }
+
+    fun handleHealthConnectPermissions(activity: Activity) {
+        if (activityResultCallback == null) {
+            activityResultCallback = Consumer {
+                permGroupsToSkip.add(HEALTH_PERMISSION_GROUP)
+                requestInfosLiveData.update()
+            }
+            val healthPermissions = unfilteredAffectedPermissions.filter { permission ->
+                isHealthPermission(activity, permission)
+            }.toTypedArray()
+            val intent: Intent = Intent(ACTION_MANAGE_HEALTH_PERMISSIONS)
+                .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
+                .putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, healthPermissions)
+                .putExtra(Intent.EXTRA_USER, Process.myUserHandle())
+                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+            activity.startActivityForResult(intent, APP_PERMISSION_REQUEST_CODE)
+        }
+    }
+
     /**
      * Send the user directly to the AppPermissionFragment. Used for R+ apps.
      *
@@ -1095,7 +1286,6 @@
      */
     fun sendDirectlyToSettings(activity: Activity, groupName: String) {
         if (activityResultCallback == null) {
-            startAppPermissionFragment(activity, groupName)
             activityResultCallback = Consumer { data ->
                 if (data?.getStringExtra(EXTRA_RESULT_PERMISSION_INTERACTED) == null) {
                     // User didn't interact, count against rate limit
@@ -1114,9 +1304,36 @@
                 // Update our liveData now that there is a new skipped group
                 requestInfosLiveData.update()
             }
+            startAppPermissionFragment(activity, groupName)
         }
     }
 
+    private fun shouldShowMorePhotosMessage(group: LightAppPermGroup): Boolean {
+        return isVisualUserSelectedOnlyGranted(group) &&
+                group.permissions[READ_MEDIA_VISUAL_USER_SELECTED]?.isImplicit == true &&
+                group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU
+    }
+
+    fun openPhotoPicker(activity: Activity, result: Int) {
+        if (activityResultCallback != null) {
+            return
+        }
+        val permissions = groupStates[READ_MEDIA_VISUAL to false]?.affectedPermissions ?: return
+        activityResultCallback = Consumer { data ->
+            val anySelected = data?.getBooleanExtra(INTENT_PHOTOS_SELECTED, true) == true
+            if (anySelected) {
+                onPermissionGrantResult(READ_MEDIA_VISUAL, null, result)
+            } else {
+                onPermissionGrantResult(READ_MEDIA_VISUAL, null, CANCELED)
+            }
+            logPhotoPickerInteraction(result)
+            requestInfosLiveData.update()
+        }
+        activity.startActivityForResult(Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP)
+            .putExtra(Intent.EXTRA_UID, packageInfo.uid)
+            .setType(KotlinUtils.getMimeTypeForPermissions(permissions)), PHOTO_PICKER_REQUEST_CODE)
+    }
+
     /**
      * Send the user to the AppPermissionFragment from a link. Used for Q- apps
      *
@@ -1136,6 +1353,26 @@
         }
     }
 
+    /**
+    * Shows the Permission Rationale Dialog. For use with U+ only, otherwise no-op.
+    *
+    * @param activity The current activity
+    * @param groupName The name of the permission group whose fragment should be opened
+    */
+    fun showPermissionRationaleActivity(activity: Activity, groupName: String) {
+        if (!SdkLevel.isAtLeastU()) {
+            return
+        }
+
+        val intent = Intent(activity, PermissionRationaleActivity::class.java).apply {
+            putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
+            putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName)
+            putExtra(Constants.EXTRA_SESSION_ID, sessionId)
+        }
+        // TODO(b/260789748): setup similar result callback as the sendToSettingsFromLink method
+        activity.startActivity(intent)
+    }
+
     private fun startAppPermissionFragment(activity: Activity, groupName: String) {
         val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSION)
             .putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
@@ -1152,6 +1389,20 @@
         return "${this::class.java.name}_${groupName}_$isBackground"
     }
 
+    private fun logPhotoPickerInteraction(result: Int) {
+        val foregroundGroupState = groupStates[READ_MEDIA_VISUAL to false] ?: return
+        when (result) {
+            GRANTED_USER_SELECTED -> {
+                reportRequestResult(foregroundGroupState.affectedPermissions,
+                    PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__PHOTOS_SELECTED)
+            }
+            CANCELED -> {
+                reportRequestResult(foregroundGroupState.affectedPermissions,
+                    PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED)
+            }
+        }
+    }
+
     private fun logSettingsInteraction(groupName: String, result: Int) {
         val foregroundGroupState = groupStates[groupName to false]
         val backgroundGroupState = groupStates[groupName to true]
@@ -1237,18 +1488,12 @@
                     "initialized", IllegalStateException())
             return
         }
-        var selectedLocations = 0
-        // log permissions if it's 1) first time requesting both locations OR 2) upgrade flow
-        if (isFirstTimeRequestingFineAndCoarse ||
-                selectedPrecision ==
-                    1 shl PERMISSION_TO_BIT_SHIFT[ACCESS_FINE_LOCATION]!!) {
-            selectedLocations = selectedPrecision
-        }
+
         PermissionControllerStatsLog.write(GRANT_PERMISSIONS_ACTIVITY_BUTTON_ACTIONS,
                 groupName, packageInfo.uid, packageName, presentedButtons, clickedButton, sessionId,
-                packageInfo.targetSdkVersion, selectedLocations)
+                packageInfo.targetSdkVersion, selectedPrecision)
         Log.v(LOG_TAG, "Logged buttons presented and clicked permissionGroupName=" +
-                "$groupName uid=${packageInfo.uid} selectedLocations=$selectedLocations " +
+                "$groupName uid=${packageInfo.uid} selectedPrecision=$selectedPrecision " +
                 "package=$packageName presentedButtons=$presentedButtons " +
                 "clickedButton=$clickedButton sessionId=$sessionId " +
                 "targetSdk=${packageInfo.targetSdkVersion}")
@@ -1262,7 +1507,8 @@
     }
 
     companion object {
-        private const val APP_PERMISSION_REQUEST_CODE = 1
+        const val APP_PERMISSION_REQUEST_CODE = 1
+        const val PHOTO_PICKER_REQUEST_CODE = 2
         private const val STATE_UNKNOWN = 0
         private const val STATE_ALLOWED = 1
         private const val STATE_DENIED = 2
@@ -1281,10 +1527,11 @@
             FG_FINE_LOCATION_MESSAGE(4),
             FG_COARSE_LOCATION_MESSAGE(5),
             STORAGE_SUPERGROUP_MESSAGE_Q_TO_S(6),
-            STORAGE_SUPERGROUP_MESSAGE_PRE_Q(7);
+            STORAGE_SUPERGROUP_MESSAGE_PRE_Q(7),
+            MORE_PHOTOS_MESSAGE(8)
         }
 
-        fun filterNotificationPermissionIfNeededSync(
+        fun filterPermissionsIfNeededSync(
             packageName: String,
             permissions: Array<String>?
         ): Array<String>? {
@@ -1292,17 +1539,19 @@
                 return null
             }
 
-            try {
-                val targetSdk = PermissionControllerApplication.get().packageManager
+            val targetSdk = try {
+                PermissionControllerApplication.get().packageManager
                         .getPackageInfo(packageName, 0).applicationInfo.targetSdkVersion
-                if (targetSdk > Build.VERSION_CODES.S_V2) {
-                    return permissions
-                }
             } catch (e: PackageManager.NameNotFoundException) {
-                return permissions
+                Build.VERSION_CODES.TIRAMISU
+            }
+            var permsList = permissions.toMutableList()
+
+            if (targetSdk < Build.VERSION_CODES.TIRAMISU) {
+                permsList.remove(POST_NOTIFICATIONS)
             }
 
-            return permissions.toList().filter { it != POST_NOTIFICATIONS }.toTypedArray()
+            return permsList.toTypedArray()
         }
     }
 }
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt
index 682ec10..dd89b74 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ManageStandardPermissionsViewModel.kt
@@ -19,6 +19,7 @@
 import android.Manifest
 import android.app.Application
 import android.content.Intent
+import android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP
 import android.os.Bundle
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.AndroidViewModel
@@ -73,6 +74,11 @@
             Utils.navigateToNotificationSettings(fragment.context!!)
             return
         }
+        if (Utils.isHealthPermissionUiEnabled() &&
+                groupName == HEALTH_PERMISSION_GROUP) {
+            Utils.navigateToHealthConnectSettings(fragment.context!!)
+            return
+        }
         fragment.findNavController().navigateSafe(R.id.manage_to_perm_apps, args)
     }
 
@@ -99,4 +105,4 @@
     override fun onUpdate() {
         value = customPermGroupPackages.value?.size ?: 0
     }
-}
\ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt
index 9814a11..cd1a936 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt
@@ -38,25 +38,25 @@
 import com.android.modules.utils.build.SdkLevel
 import com.android.permissioncontroller.PermissionControllerStatsLog
 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED
-import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__UNDEFINED
 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND
 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__DENIED
+import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_APPS_FRAGMENT_VIEWED__CATEGORY__UNDEFINED
 import com.android.permissioncontroller.R
 import com.android.permissioncontroller.permission.data.AllPackageInfosLiveData
 import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData
 import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData.FullStoragePackageState
 import com.android.permissioncontroller.permission.data.SinglePermGroupPackagesUiInfoLiveData
-import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage
 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState
+import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage
 import com.android.permissioncontroller.permission.ui.Category
 import com.android.permissioncontroller.permission.ui.LocationProviderInterceptDialog
-import com.android.permissioncontroller.permission.ui.handheld.v31.is7DayToggleEnabled
 import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.CREATION_LOGGED_KEY
 import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.HAS_SYSTEM_APPS_KEY
 import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.SHOULD_SHOW_SYSTEM_KEY
 import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel.Companion.SHOW_ALWAYS_ALLOWED
 import com.android.permissioncontroller.permission.utils.KotlinUtils.getPackageUid
+import com.android.permissioncontroller.permission.utils.KotlinUtils.is7DayToggleEnabled
 import com.android.permissioncontroller.permission.utils.LocationUtils
 import com.android.permissioncontroller.permission.utils.Utils
 import com.android.permissioncontroller.permission.utils.navigateSafe
@@ -169,8 +169,8 @@
         }
     }
 
-    inner class CategorizedAppsLiveData(groupName: String)
-        : MediatorLiveData<@kotlin.jvm.JvmSuppressWildcards
+    inner class CategorizedAppsLiveData(groupName: String) :
+        MediatorLiveData<@kotlin.jvm.JvmSuppressWildcards
     Map<Category, List<Pair<String, UserHandle>>>>() {
         private val packagesUiInfoLiveData = SinglePermGroupPackagesUiInfoLiveData[groupName]
 
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java
index b69fb66..4006bcf 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreference.java
@@ -44,8 +44,12 @@
         setTitle(label);
         setIcon(tintedIcon);
         setIntent(managePgIntent);
-        updateSummary(permissionGroupInfo.getNonSystemGranted(),
-                permissionGroupInfo.getNonSystemUserSetOrPreGranted());
+        updateSummary(permissionGroupInfo);
+    }
+
+    void updateSummary(PermGroupPackagesUiInfo info) {
+        updateSummary(info.getNonSystemGranted(),
+                info.getNonSystemTotal());
     }
 
     void updateSummary(int granted, int used) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java
index 49a01ef..3541ede 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionGroupPreferenceUtils.java
@@ -60,8 +60,7 @@
             if (preference == null) {
                 preference = new PermissionGroupPreference(context, info);
             } else {
-                preference.updateSummary(info.getNonSystemGranted(),
-                        info.getNonSystemUserSetOrPreGranted());
+                preference.updateSummary(info);
                 // Reset the ordering back to default, so that when we add it back it falls into the
                 // right place, and the preferences are ordered as we add them.
                 preference.setOrder(Preference.DEFAULT_ORDER);
@@ -99,8 +98,7 @@
             final PermissionGroupPreference preference =
                     (PermissionGroupPreference) preferenceGroup.getPreference(i);
             final PermGroupPackagesUiInfo info = permissionGroups.get(i);
-            preference.updateSummary(info.getNonSystemGranted(),
-                    info.getNonSystemUserSetOrPreGranted());
+            preference.updateSummary(info);
         }
     }
 
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewOngoingUsageViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewOngoingUsageViewModel.kt
index e65c5c1..309b9eb 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewOngoingUsageViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewOngoingUsageViewModel.kt
@@ -47,15 +47,15 @@
 import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData
 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
 import com.android.permissioncontroller.permission.data.micMutedLiveData
-import com.android.permissioncontroller.permission.ui.handheld.v31.shouldShowLocationIndicators
-import com.android.permissioncontroller.permission.ui.handheld.v31.shouldShowPermissionsDashboard
 import com.android.permissioncontroller.permission.ui.handheld.v31.ReviewOngoingUsageFragment.PHONE_CALL
 import com.android.permissioncontroller.permission.ui.handheld.v31.ReviewOngoingUsageFragment.VIDEO_CALL
 import com.android.permissioncontroller.permission.utils.KotlinUtils
+import com.android.permissioncontroller.permission.utils.KotlinUtils.shouldShowLocationIndicators
+import com.android.permissioncontroller.permission.utils.KotlinUtils.shouldShowPermissionsDashboard
 import com.android.permissioncontroller.permission.utils.Utils
-import kotlinx.coroutines.Job
 import java.time.Instant
 import kotlin.math.max
+import kotlinx.coroutines.Job
 
 private const val FIRST_OPENED_KEY = "FIRST_OPENED"
 private const val CALL_OP_USAGE_KEY = "CALL_OP_USAGE"
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt
index 97c5d75..4e1fc18 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/ReviewPermissionsViewModel.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.permission.ui.model.v33
+package com.android.permissioncontroller.permission.ui.model
 
 import android.app.Application
 import android.content.Context
@@ -26,13 +26,13 @@
 import androidx.navigation.NavController
 import androidx.navigation.fragment.NavHostFragment
 import com.android.permissioncontroller.R
-import com.android.permissioncontroller.permission.utils.PermissionMapping
 import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData
 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData.Companion.NON_RUNTIME_NORMAL_PERMS
 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
 import com.android.permissioncontroller.permission.data.get
 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
+import com.android.permissioncontroller.permission.utils.PermissionMapping
 import com.android.permissioncontroller.permission.utils.Utils
 import com.android.permissioncontroller.permission.utils.navigateSafe
 import com.android.settingslib.RestrictedLockUtils
@@ -326,4 +326,4 @@
         @Suppress("UNCHECKED_CAST")
         return ReviewPermissionsViewModel(app, packageInfo = packageInfo) as T
     }
-}
\ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt
index af8e689..f9852d2 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageDetailsViewModel.kt
@@ -38,8 +38,8 @@
 import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage.TimelineUsage
 import com.android.permissioncontroller.permission.model.v31.PermissionUsages
 import com.android.permissioncontroller.permission.ui.handheld.v31.getDurationUsedStr
-import com.android.permissioncontroller.permission.ui.handheld.v31.is7DayToggleEnabled
 import com.android.permissioncontroller.permission.ui.handheld.v31.shouldShowSubattributionInPermissionsDashboard
+import com.android.permissioncontroller.permission.utils.KotlinUtils
 import com.android.permissioncontroller.permission.utils.KotlinUtils.getPackageLabel
 import com.android.permissioncontroller.permission.utils.PermissionMapping
 import com.android.permissioncontroller.permission.utils.StringUtils
@@ -108,7 +108,7 @@
         show7Days: Boolean
     ): PermissionUsageDetailsUiData {
         val showPermissionUsagesDuration =
-            if (is7DayToggleEnabled() && show7Days) {
+            if (KotlinUtils.is7DayToggleEnabled() && show7Days) {
                 TIME_7_DAYS_DURATION
             } else {
                 TIME_24_HOURS_DURATION
@@ -116,7 +116,7 @@
         val startTime =
             (System.currentTimeMillis() - showPermissionUsagesDuration).coerceAtLeast(
                 Instant.EPOCH.toEpochMilli())
-        val appPermissionTimelineUsages: List<AppPermissionTimelineUsages> =
+        val appPermissionTimelineUsages: List<AppPermissionTimelineUsage> =
             extractAppPermissionTimelineUsagesForGroup(appPermissionUsages, permissionGroup)
         val shouldDisplayShowSystemToggle =
             shouldDisplayShowSystemToggle(appPermissionTimelineUsages)
@@ -147,16 +147,16 @@
 
         return HistoryPreferenceData(
             UserHandle.getUserHandleForUid(
-                discreteAccessClusterData.appPermissionTimelineUsages.permissionApp.uid),
-            discreteAccessClusterData.appPermissionTimelineUsages.permissionApp.packageName,
-            discreteAccessClusterData.appPermissionTimelineUsages.permissionApp.icon,
-            discreteAccessClusterData.appPermissionTimelineUsages.permissionApp.label,
+                discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.uid),
+            discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.packageName,
+            discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.icon,
+            discreteAccessClusterData.appPermissionTimelineUsage.permissionApp.label,
             permissionGroup,
-            discreteAccessClusterData.endTime,
+            discreteAccessClusterData.discreteAccessDataList.last().accessTimeMs,
+            discreteAccessClusterData.discreteAccessDataList.first().accessTimeMs,
             summary,
             showingSubattribution,
-            accessTimeList,
-            discreteAccessClusterData.appPermissionTimelineUsages.attributionTags,
+            discreteAccessClusterData.appPermissionTimelineUsage.attributionTags,
             sessionId)
     }
 
@@ -169,11 +169,11 @@
         groupName: String,
     ) = appPermissionUsages.extractAllPlatformAppPermissionGroups().any { it.name == groupName }
 
-    /** Extracts a list of [AppPermissionTimelineUsages] for a particular permission group. */
+    /** Extracts a list of [AppPermissionTimelineUsage] for a particular permission group. */
     private fun extractAppPermissionTimelineUsagesForGroup(
         appPermissionUsages: List<AppPermissionUsage>,
         group: String
-    ): List<AppPermissionTimelineUsages> =
+    ): List<AppPermissionTimelineUsage> =
         appPermissionUsages
             .filter { !Utils.getExemptedPackages(roleManager).contains(it.packageName) }
             .map { appPermissionUsage ->
@@ -185,11 +185,10 @@
 
     /** Returns whether the show/hide system toggle should be displayed in the UI. */
     private fun shouldDisplayShowSystemToggle(
-        appPermissionTimelineUsages: List<AppPermissionTimelineUsages>,
+        appPermissionTimelineUsages: List<AppPermissionTimelineUsage>,
     ): Boolean =
         appPermissionTimelineUsages
-            .map { it.timelineUsages }
-            .flatten()
+            .map { it.timelineUsage }
             .filter { it.hasDiscreteData() }
             .any { it.group.isSystem() }
 
@@ -198,40 +197,34 @@
      * (recent here refers to usages occurring after the provided start time).
      */
     private fun getPermissionAppsWithRecentDiscreteUsage(
-        appPermissionTimelineUsagesList: List<AppPermissionTimelineUsages>,
+        appPermissionTimelineUsageList: List<AppPermissionTimelineUsage>,
         showSystem: Boolean,
         startTime: Long,
     ): List<PermissionApp> =
-        appPermissionTimelineUsagesList.mapNotNull { appPermissionTimelineUsages ->
-            if (appPermissionTimelineUsages.timelineUsages
-                .filter { it.hasDiscreteData() }
-                .filter { showSystem || !it.group.isSystem() }
-                .any { timelineUsage ->
-                    timelineUsage.allDiscreteAccessTime.any { it.first >= startTime }
-                })
-                appPermissionTimelineUsages.permissionApp
-            else null
-        }
+        appPermissionTimelineUsageList
+            .filter { it.timelineUsage.hasDiscreteData() }
+            .filter { showSystem || !it.timelineUsage.group.isSystem() }
+            .filter { it.timelineUsage.allDiscreteAccessTime.any { it.first >= startTime } }
+            .map { it.permissionApp }
 
     /**
      * Builds a list of [DiscreteAccessClusterData] from the provided list of
-     * [AppPermissionTimelineUsages].
+     * [AppPermissionTimelineUsage].
      */
     private fun buildDiscreteAccessClusterData(
-        appPermissionTimelineUsagesList: List<AppPermissionTimelineUsages>,
+        appPermissionTimelineUsageList: List<AppPermissionTimelineUsage>,
         showSystem: Boolean,
         startTime: Long,
     ): List<DiscreteAccessClusterData> =
-        appPermissionTimelineUsagesList
+        appPermissionTimelineUsageList
             .map { appPermissionTimelineUsages ->
                 val accessDataList =
                     extractRecentDiscreteAccessData(
-                        appPermissionTimelineUsages.timelineUsages, showSystem, startTime)
+                        appPermissionTimelineUsages.timelineUsage, showSystem, startTime)
 
                 if (accessDataList.size <= 1) {
                     return@map accessDataList.map {
-                        DiscreteAccessClusterData(
-                            appPermissionTimelineUsages, it.accessTimeMs, listOf(it))
+                        DiscreteAccessClusterData(appPermissionTimelineUsages, listOf(it))
                     }
                 }
 
@@ -239,7 +232,9 @@
             }
             .flatten()
             .sortedWith(
-                compareBy({ -it.endTime }, { it.appPermissionTimelineUsages.permissionApp.label }))
+                compareBy(
+                    { -it.discreteAccessDataList.first().accessTimeMs },
+                    { it.appPermissionTimelineUsage.permissionApp.label }))
             .toList()
 
     /**
@@ -249,26 +244,21 @@
      * in the same cluster.
      */
     private fun clusterDiscreteAccessData(
-        appPermissionTimelineUsages: AppPermissionTimelineUsages,
+        appPermissionTimelineUsage: AppPermissionTimelineUsage,
         discreteAccessDataList: List<DiscreteAccessData>
     ): List<DiscreteAccessClusterData> {
         val clusterDataList = mutableListOf<DiscreteAccessClusterData>()
-        var currentAccessTimeMs: Long = 0
         val currentDiscreteAccessDataList: MutableList<DiscreteAccessData> = mutableListOf()
         for (discreteAccessData in discreteAccessDataList) {
             if (currentDiscreteAccessDataList.isEmpty()) {
                 currentDiscreteAccessDataList.add(discreteAccessData)
-                currentAccessTimeMs = discreteAccessData.accessTimeMs
             } else if (!canAccessBeAddedToCluster(
                 discreteAccessData, currentDiscreteAccessDataList)) {
                 clusterDataList.add(
                     DiscreteAccessClusterData(
-                        appPermissionTimelineUsages,
-                        currentAccessTimeMs,
-                        currentDiscreteAccessDataList.toMutableList()))
+                        appPermissionTimelineUsage, currentDiscreteAccessDataList.toMutableList()))
                 currentDiscreteAccessDataList.clear()
                 currentDiscreteAccessDataList.add(discreteAccessData)
-                currentAccessTimeMs = discreteAccessData.accessTimeMs
             } else {
                 currentDiscreteAccessDataList.add(discreteAccessData)
             }
@@ -276,9 +266,7 @@
         if (currentDiscreteAccessDataList.isNotEmpty()) {
             clusterDataList.add(
                 DiscreteAccessClusterData(
-                    appPermissionTimelineUsages,
-                    currentAccessTimeMs,
-                    currentDiscreteAccessDataList))
+                    appPermissionTimelineUsage, currentDiscreteAccessDataList))
         }
         return clusterDataList
     }
@@ -289,18 +277,19 @@
      * provided start time).
      */
     private fun extractRecentDiscreteAccessData(
-        timelineUsages: List<TimelineUsage>,
+        timelineUsages: TimelineUsage,
         showSystem: Boolean,
         startTime: Long
-    ): List<DiscreteAccessData> =
-        timelineUsages
-            .asSequence()
-            .filter { it.hasDiscreteData() }
-            .filter { showSystem || !it.group.isSystem() }
-            .map { getRecentDiscreteAccessData(it, startTime) }
-            .flatten()
-            .sortedWith(compareBy { -it.accessTimeMs })
-            .toList()
+    ): List<DiscreteAccessData> {
+        return if (timelineUsages.hasDiscreteData() &&
+            (showSystem || !timelineUsages.group.isSystem())) {
+            getRecentDiscreteAccessData(timelineUsages, startTime)
+                .sortedWith(compareBy { -it.accessTimeMs })
+                .toList()
+        } else {
+            listOf()
+        }
+    }
 
     /**
      * Extract recent [DiscreteAccessData] from a [TimelineUsage]. (recent here refers to accesses
@@ -392,10 +381,10 @@
 
     /** Returns the attribution label for the permission access, if any. */
     private fun getSubattributionLabel(usage: DiscreteAccessClusterData): String? =
-        if (usage.appPermissionTimelineUsages.label == Resources.ID_NULL) null
+        if (usage.appPermissionTimelineUsage.label == Resources.ID_NULL) null
         else
-            usage.appPermissionTimelineUsages.permissionApp.attributionLabels?.let {
-                it[usage.appPermissionTimelineUsages.label]
+            usage.appPermissionTimelineUsage.permissionApp.attributionLabels?.let {
+                it[usage.appPermissionTimelineUsage.label]
             }
 
     /** Builds a summary of the permission access. */
@@ -426,27 +415,25 @@
     }
 
     /**
-     * Builds a list of [AppPermissionTimelineUsages] from the provided
+     * Builds a list of [AppPermissionTimelineUsage] from the provided
      * [AppPermissionUsage.GroupUsage].
      */
     private fun getAppPermissionTimelineUsages(
         app: PermissionApp,
         groupUsage: AppPermissionUsage.GroupUsage?
-    ): List<AppPermissionTimelineUsages> {
+    ): List<AppPermissionTimelineUsage> {
         if (groupUsage == null) {
             return listOf()
         }
 
         if (shouldShowSubattributionForApp(app.appInfo)) {
             return groupUsage.attributionLabelledGroupUsages.map {
-                AppPermissionTimelineUsages(
-                    permissionGroup, app, listOf<TimelineUsage>(it), it.label)
+                AppPermissionTimelineUsage(permissionGroup, app, it, it.label)
             }
         }
 
         return listOf(
-            AppPermissionTimelineUsages(
-                permissionGroup, app, listOf<TimelineUsage>(groupUsage), Resources.ID_NULL))
+            AppPermissionTimelineUsage(permissionGroup, app, groupUsage, Resources.ID_NULL))
     }
 
     /** Extracts to a set all the permission groups declared by the platform. */
@@ -480,10 +467,10 @@
         val appIcon: Drawable?,
         val preferenceTitle: String,
         val permissionGroup: String,
+        val accessStartTime: Long,
         val accessEndTime: Long,
         val summaryText: CharSequence?,
         val showingAttribution: Boolean,
-        val accessTimeList: List<Long>,
         val attributionTags: ArrayList<String>,
         val sessionId: Long
     )
@@ -526,8 +513,7 @@
      * Data class representing a cluster of accesses, to be represented as a single entry in the UI.
      */
     data class DiscreteAccessClusterData(
-        val appPermissionTimelineUsages: AppPermissionTimelineUsages,
-        val endTime: Long,
+        val appPermissionTimelineUsage: AppPermissionTimelineUsage,
         val discreteAccessDataList: List<DiscreteAccessData>
     )
 
@@ -539,19 +525,19 @@
     )
 
     /** Data class representing an app's permissions usages for a particular permission group. */
-    data class AppPermissionTimelineUsages(
+    data class AppPermissionTimelineUsage(
         /** Permission group whose usage is being tracked. */
         val permissionGroup: String,
         // we need a PermissionApp because the loader takes the PermissionApp
         // object and loads the icon and label information asynchronously
         /** App whose permissions are being tracked. */
         val permissionApp: PermissionApp,
-        /** Timeline usages for the given app and permission. */
-        val timelineUsages: List<TimelineUsage>,
+        /** Timeline usage for the given app and permission. */
+        val timelineUsage: TimelineUsage,
         val label: Int
     ) {
         val attributionTags: java.util.ArrayList<String>
-            get() = ArrayList(timelineUsages.mapNotNull { it.attributionTags }.flatten())
+            get() = ArrayList(timelineUsage.attributionTags)
     }
 }
 
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt
index 1c27f2b..6287be2 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt
@@ -24,13 +24,13 @@
 import androidx.annotation.RequiresApi
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
-import com.android.permissioncontroller.permission.utils.PermissionMapping
 import com.android.permissioncontroller.permission.model.AppPermissionGroup
 import com.android.permissioncontroller.permission.model.legacy.PermissionApps.PermissionApp
 import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage
 import com.android.permissioncontroller.permission.model.v31.PermissionUsages
-import com.android.permissioncontroller.permission.ui.handheld.v31.is7DayToggleEnabled
+import com.android.permissioncontroller.permission.utils.KotlinUtils
 import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupLabel
+import com.android.permissioncontroller.permission.utils.PermissionMapping
 import com.android.permissioncontroller.permission.utils.Utils
 import java.time.Instant
 import java.util.concurrent.TimeUnit
@@ -48,7 +48,8 @@
         private val TIME_FILTER_MILLIS = TimeUnit.DAYS.toMillis(7)
         private val TIME_7_DAYS_DURATION = TimeUnit.DAYS.toMillis(7)
         private val TIME_24_HOURS_DURATION = TimeUnit.DAYS.toMillis(1)
-
+        /** Permission groups that should be hidden from the permissions usage UI. */
+        private val EXEMPTED_PERMISSION_GROUPS = setOf(Manifest.permission_group.NOTIFICATIONS)
         @JvmStatic
                 /** Map to represent ordering for permission groups in the permissions usage UI. */
         val PERMISSION_GROUP_ORDER: Map<String, Int> =
@@ -92,7 +93,7 @@
     ): PermissionUsagesUiData {
         val curTime = System.currentTimeMillis()
         val showPermissionUsagesDuration =
-            if (is7DayToggleEnabled() && show7Days) {
+            if (KotlinUtils.is7DayToggleEnabled() && show7Days) {
                 TIME_7_DAYS_DURATION
             } else {
                 TIME_24_HOURS_DURATION
@@ -101,39 +102,66 @@
 
         val filteredAppPermissionUsages =
             appPermissionUsages.filter { !exemptedPackages.contains(it.packageName) }
-        val permissionGroupsToUsageCountMap =
-            filteredAppPermissionUsages.buildPermissionGroupsUsageCountMap(startTime, showSystem)
         val displayShowSystemToggle: Boolean =
             filteredAppPermissionUsages.displayShowSystemToggle(startTime)
         val permissionApps = filteredAppPermissionUsages.getRecentPermissionApps(startTime)
         val orderedPermissionGroupsWithUsage =
-            buildOrderedPermissionGroupsWithUsageCount(context, permissionGroupsToUsageCountMap)
+            filteredAppPermissionUsages.buildOrderedPermissionGroupsWithUsageCount(
+                context,
+                startTime,
+                showSystem
+            )
 
         return PermissionUsagesUiData(
-            permissionGroupsToUsageCountMap,
             permissionApps,
             displayShowSystemToggle,
-            orderedPermissionGroupsWithUsage)
+            orderedPermissionGroupsWithUsage
+        )
     }
 
-    /** Build a map of permission groups to the number of apps that recently accessed them. */
-    private fun List<AppPermissionUsage>.buildPermissionGroupsUsageCountMap(
+    /**
+     * Creates an ordered list of [PermissionGroupWithUsageCount] instances to show in the UI,
+     * representing a mapping of permission groups to the number of apps that recently accessed
+     * them.
+     *
+     * The list is ordered as follows:
+     *
+     * 1. Location
+     * 2. Camera
+     * 3. Microphone
+     * 4. Remaining permission groups, ordered alphabetically
+     */
+    private fun List<AppPermissionUsage>.buildOrderedPermissionGroupsWithUsageCount(
+        context: Context,
         startTime: Long,
         showSystem: Boolean
-    ): Map<String, Int> {
+    ): List<PermissionGroupWithUsageCount> {
         val permissionGroupsUsageCountMap: MutableMap<String, Int> = HashMap()
-        extractAllPlatformAppPermissionGroups().forEach { permissionGroupsUsageCountMap[it] = 0 }
+        extractPlatformAppPermissionGroupsToDisplay().forEach {
+            permissionGroupsUsageCountMap[it] = 0
+        }
 
         for (appUsage in this) {
             appUsage.groupUsages
                 .filter { showSystem || !it.group.isSystem() }
+                .filter { !EXEMPTED_PERMISSION_GROUPS.contains(it.group.name) }
                 .filter { it.lastAccessTime >= startTime }
                 .forEach {
                     permissionGroupsUsageCountMap[it.group.name] =
                         permissionGroupsUsageCountMap.getOrDefault(it.group.name, 0) + 1
                 }
         }
-        return permissionGroupsUsageCountMap
+        return permissionGroupsUsageCountMap.entries.map {
+            PermissionGroupWithUsageCount(
+                it.key,
+                it.value
+            )
+        }
+            .sortedWith(
+                compareBy(
+                    { PERMISSION_GROUP_ORDER.getOrDefault(it.permGroup, DEFAULT_ORDER) },
+                    { getPermGroupLabel(context, it.permGroup).toString() })
+            )
     }
 
     /** Extracts [PermissionApp] where there has been recent permission usage. */
@@ -141,9 +169,11 @@
         startTime: Long,
     ): java.util.ArrayList<PermissionApp> {
         return ArrayList(
-            filter {
-                    it.groupUsages.any { it.lastAccessTime >= startTime || it.lastAccessTime == 0L }
-                }
+            filter { appPermissionUsage ->
+                appPermissionUsage.groupUsages
+                    .filter { !EXEMPTED_PERMISSION_GROUPS.contains(it.group.name) }
+                    .any { it.lastAccessTime >= startTime || it.lastAccessTime == 0L }
+            }
                 .map { it.app })
     }
 
@@ -155,36 +185,21 @@
         startTime: Long,
     ): Boolean {
         return flatMap { it.groupUsages }
+            .filter { !EXEMPTED_PERMISSION_GROUPS.contains(it.group.name) }
             .filter { it.lastAccessTime > startTime && it.lastAccessTime > 0L }
             .any { it.group.isSystem() }
     }
 
     /**
-     * Creates an ordered list of [PermissionGroupWithUsageCount] instances to show in the UI.
-     *
-     * The list is ordered as follows:
-     *
-     * 1. Location
-     * 2. Camera
-     * 3. Microphone
-     * 4. Remaining permission groups, ordered alphabetically
+     * Extracts to a set all the permission groups declared by the platform that should be displayed
+     * in the UI.
      */
-    private fun buildOrderedPermissionGroupsWithUsageCount(
-        context: Context,
-        permissionUsageAppCountMap: Map<String, Int>
-    ): List<PermissionGroupWithUsageCount> =
-        ArrayList(permissionUsageAppCountMap.entries)
-            .map { PermissionGroupWithUsageCount(it.key, it.value) }
-            .sortedWith(
-                compareBy(
-                    { PERMISSION_GROUP_ORDER.getOrDefault(it.permGroup, DEFAULT_ORDER) },
-                    { getPermGroupLabel(context, it.permGroup).toString() }))
-
-    /** Extracts to a set all the permission groups declared by the platform. */
-    private fun List<AppPermissionUsage>.extractAllPlatformAppPermissionGroups(): Set<String> =
+    private fun List<AppPermissionUsage>.extractPlatformAppPermissionGroupsToDisplay():
+            Set<String> =
         this.flatMap { it.groupUsages }
             .map { it.group.name }
             .filter { PermissionMapping.isPlatformPermissionGroup(it) }
+            .filter { !EXEMPTED_PERMISSION_GROUPS.contains(it) }
             .toSet()
 
     /**
@@ -198,10 +213,6 @@
 
     /** Data class to hold all the information required to configure the UI. */
     data class PermissionUsagesUiData(
-        // TODO(b/243970988): Remove permissionGroupUsageCounts from this data class as it
-        //  duplicates information in orderedPermissionGroupWithUsage.
-        /** Map from permission group to count of apps that recently used the permissions. */
-        val permissionGroupUsageCounts: Map<String, Int>,
         /** List of [PermissionApp] instances */
         // Note that these are used only to cache app data for the permission usage details
         // fragment, and have no bearing on the UI on the main permission usage page.
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModelNew.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModelNew.kt
new file mode 100644
index 0000000..223ebef
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModelNew.kt
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.permission.ui.model.v31
+
+import android.Manifest
+import android.app.Application
+import android.app.role.RoleManager
+import android.os.Build
+import android.os.Bundle
+import android.os.UserHandle
+import androidx.annotation.RequiresApi
+import androidx.lifecycle.AbstractSavedStateViewModelFactory
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.savedstate.SavedStateRegistryOwner
+import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData
+import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
+import com.android.permissioncontroller.permission.data.v31.AllLightPackageOpsLiveData
+import com.android.permissioncontroller.permission.model.livedatatypes.v31.LightPackageOps
+import com.android.permissioncontroller.permission.utils.KotlinUtils
+import com.android.permissioncontroller.permission.utils.Utils
+import java.time.Instant
+import java.util.concurrent.TimeUnit
+import kotlin.math.max
+
+/**
+ * [ViewModel] for handheld Permissions Usage UI.
+ *
+ * Note that this class replaces [PermissionUsageViewModel] to rely on [LiveData] instead of
+ * [PermissionUsages] loader.
+ */
+// TODO(b/257317510): Remove "new" suffix and deprecate PermissionUsageViewModel.
+class PermissionUsageViewModelNew(
+    private val state: SavedStateHandle,
+    app: Application,
+) : AndroidViewModel(app) {
+    private val roleManager =
+        Utils.getSystemServiceSafe(app.applicationContext, RoleManager::class.java)
+    private val exemptedPackages: Set<String> = Utils.getExemptedPackages(roleManager)
+
+    private val mAllLightPackageOpsLiveData = AllLightPackageOpsLiveData(app)
+    private val appPermGroupUiInfoLiveDataList =
+        mutableMapOf<AppPermissionId, AppPermGroupUiInfoLiveData>()
+
+    val showSystemLiveData = state.getLiveData(SHOULD_SHOW_SYSTEM_KEY, false)
+    val show7DaysLiveData = state.getLiveData(SHOULD_SHOW_7_DAYS_KEY, false)
+
+    /** Updates whether system app permissions usage should be displayed in the UI. */
+    fun updateShowSystem(showSystem: Boolean) {
+        if (showSystem != state[SHOULD_SHOW_SYSTEM_KEY]) {
+            state[SHOULD_SHOW_SYSTEM_KEY] = showSystem
+        }
+    }
+
+    /** Updates whether 7 days usage or 1 day usage should be displayed in the UI. */
+    fun updateShow7Days(show7Days: Boolean) {
+        if (show7Days != state[SHOULD_SHOW_7_DAYS_KEY]) {
+            state[SHOULD_SHOW_7_DAYS_KEY] = show7Days
+        }
+    }
+
+    /** Builds a [PermissionUsagesUiData] containing all the data necessary to render the UI. */
+    private fun buildPermissionUsagesUiData(): PermissionUsagesUiData {
+        val curTime = System.currentTimeMillis()
+        val showSystem: Boolean = state[SHOULD_SHOW_SYSTEM_KEY] ?: false
+        val show7Days: Boolean = state[SHOULD_SHOW_7_DAYS_KEY] ?: false
+        val showPermissionUsagesDuration =
+            if (KotlinUtils.is7DayToggleEnabled() && show7Days) {
+                TIME_7_DAYS_DURATION
+            } else {
+                TIME_24_HOURS_DURATION
+            }
+        val startTime = max(curTime - showPermissionUsagesDuration, Instant.EPOCH.toEpochMilli())
+        return PermissionUsagesUiData(
+            mAllLightPackageOpsLiveData.displayShowSystemToggle(startTime),
+            showSystem,
+            show7Days,
+            mAllLightPackageOpsLiveData.buildPermissionGroupsWithUsageCounts(startTime, showSystem))
+    }
+
+    /** Builds a map of permission groups to the number of apps that recently accessed them. */
+    private fun AllLightPackageOpsLiveData.buildPermissionGroupsWithUsageCounts(
+        startTime: Long,
+        showSystem: Boolean,
+    ): Map<String, Int> {
+        val permissionUsageCountMap: MutableMap<String, Int> = HashMap()
+        for (permissionGroup: String in getAllEligiblePermissionGroups()) {
+            permissionUsageCountMap[permissionGroup] = 0
+        }
+
+        val eligibleLightPackageOpsList: List<LightPackageOps> =
+            getAllLightPackageOps()?.filterOutExemptedApps() ?: listOf()
+
+        for (lightPackageOps: LightPackageOps in eligibleLightPackageOpsList) {
+            val permGroupsToLastAccess: List<Map.Entry<String, Long>> =
+                lightPackageOps.lastPermissionGroupAccessTimesMs.entries
+                    .filterOutExemptedPermissionGroupsFromKeys()
+                    .filterOutSystemAppPermissionsIfNecessary(
+                        showSystem, lightPackageOps.packageName, lightPackageOps.userHandle)
+                    .filterAccessTimeLaterThan(startTime)
+            val recentlyUsedPermissions: List<String> = permGroupsToLastAccess.map { it.key }
+
+            for (permissionGroup: String in recentlyUsedPermissions) {
+                permissionUsageCountMap[permissionGroup] =
+                    permissionUsageCountMap.getOrDefault(permissionGroup, 0) + 1
+            }
+        }
+        return permissionUsageCountMap
+    }
+
+    /**
+     * Determines whether there are any system app permissions with recent usage, in which case the
+     * "show/hide system" toggle should be displayed in the UI.
+     */
+    private fun AllLightPackageOpsLiveData.displayShowSystemToggle(startTime: Long): Boolean {
+        val eligibleLightPackageOpsList: List<LightPackageOps> =
+            getAllLightPackageOps()?.filterOutExemptedApps() ?: listOf()
+
+        for (lightPackageOps: LightPackageOps in eligibleLightPackageOpsList) {
+            val recentlyUsedPermissions: Set<String> =
+                lightPackageOps.lastPermissionGroupAccessTimesMs.entries
+                    .filterAccessTimeLaterThan(startTime)
+                    .map { it.key }
+                    .toSet()
+            if (recentlyUsedPermissions
+                .filterOutExemptedPermissionGroups()
+                .containsSystemAppPermission(
+                    lightPackageOps.packageName, lightPackageOps.userHandle)) {
+                return true
+            }
+        }
+
+        return false
+    }
+
+    /**
+     * Returns all permission groups tracked in the [AllLightPackageOpsLiveData] eligible for
+     * display in the UI.
+     */
+    private fun AllLightPackageOpsLiveData.getAllEligiblePermissionGroups(): Set<String> {
+        val eligibleLightPackageOpsList =
+            getAllLightPackageOps()?.filterOutExemptedApps() ?: listOf()
+
+        val allPermissionGroups: Set<String> =
+            eligibleLightPackageOpsList.flatMap { it.lastPermissionGroupAccessTimesMs.keys }.toSet()
+
+        return allPermissionGroups.filterOutExemptedPermissionGroups().toSet()
+    }
+
+    private fun isAppPermissionSystem(appPermissionId: AppPermissionId): Boolean {
+        val appPermGroupUiInfo = appPermGroupUiInfoLiveDataList[appPermissionId]?.value
+
+        if (appPermGroupUiInfo != null) {
+            return appPermGroupUiInfo.isSystem
+        } else
+        // The AppPermGroupUiInfo may be null if it has either not loaded yet or if the app has not
+        // requested any permissions from the permission group in question.
+        // The Telecom doesn't request microphone or camera permissions. However, telecom app may
+        // use these permissions and they are considered system app permissions, so we return true
+        // even if the AppPermGroupUiInfo is unavailable.
+        if (appPermissionId.packageName == TELECOM_PACKAGE &&
+            (appPermissionId.permissionGroup == Manifest.permission_group.CAMERA ||
+                appPermissionId.permissionGroup == Manifest.permission_group.MICROPHONE)) {
+            return true
+        }
+        return false
+    }
+
+    private fun AllLightPackageOpsLiveData.getAllLightPackageOps() = value?.values
+
+    /**
+     * Filters out accesses earlier than the provided start time from a map of permission last
+     * accesses.
+     */
+    private fun Collection<Map.Entry<String, Long>>.filterAccessTimeLaterThan(startTime: Long) =
+        filter {
+            it.value > startTime
+        }
+
+    /**
+     * Filters out system app permissions from a map of permission last accesses, if showSystem is
+     * false.
+     */
+    private fun Collection<Map.Entry<String, Long>>.filterOutSystemAppPermissionsIfNecessary(
+        showSystem: Boolean,
+        packageName: String,
+        userHandle: UserHandle
+    ) = filter {
+        showSystem || !isAppPermissionSystem(AppPermissionId(packageName, userHandle, it.key))
+    }
+
+    /**
+     * Filters out permission groups that are exempt from permission usage tracking from a map of
+     * permission last accesses.
+     */
+    private fun Collection<Map.Entry<String, Long>>.filterOutExemptedPermissionGroupsFromKeys() =
+        filter {
+            !EXEMPTED_PERMISSION_GROUPS.contains(it.key)
+        }
+
+    /**
+     * Filters out permission groups that are exempt from permission usage tracking from a map of
+     * permission last accesses.
+     */
+    private fun Collection<String>.filterOutExemptedPermissionGroups() = filter {
+        !EXEMPTED_PERMISSION_GROUPS.contains(it)
+    }
+
+    /** Filters out [LightPackageOps] for apps that are exempt from permission usage tracking. */
+    private fun Collection<LightPackageOps>.filterOutExemptedApps() = filter {
+        !exemptedPackages.contains(it.packageName)
+    }
+
+    /**
+     * Returns from a list of permissions whether any permission along with the provided package
+     * name and user handle are considered a system app permission.
+     */
+    private fun Collection<String>.containsSystemAppPermission(
+        packageName: String,
+        userHandle: UserHandle
+    ) = any { isAppPermissionSystem(AppPermissionId(packageName, userHandle, it)) }
+
+    /** Identifier for an app permission group combination. */
+    data class AppPermissionId(
+        val packageName: String,
+        val userHandle: UserHandle,
+        val permissionGroup: String
+    )
+
+    /** Data class to hold all the information required to configure the UI. */
+    data class PermissionUsagesUiData(
+        /** Whether to show the "show/hide system" toggle. */
+        val displayShowSystemToggle: Boolean,
+        /** Whether to show system app permissions in the UI. */
+        val showSystemAppPermissions: Boolean,
+        /** Whether to show usage data for 7 days or 1 day. */
+        val show7DaysUsage: Boolean,
+        /** Map instances for display in UI */
+        val permissionGroupsWithUsageCount: Map<String, Int>,
+    )
+
+    /** LiveData object for [PermissionUsagesUiData]. */
+    val permissionUsagesUiLiveData =
+        object : SmartUpdateMediatorLiveData<@JvmSuppressWildcards PermissionUsagesUiData>() {
+
+            private var appPermGroupListPopulated: Boolean = false
+            private val getAppPermGroupUiInfoLiveData = { appPermissionId: AppPermissionId ->
+                AppPermGroupUiInfoLiveData[
+                    Triple(
+                        appPermissionId.packageName,
+                        appPermissionId.permissionGroup,
+                        appPermissionId.userHandle,
+                    )]
+            }
+
+            init {
+                addSource(mAllLightPackageOpsLiveData) { update() }
+                addSource(showSystemLiveData) { update() }
+                addSource(show7DaysLiveData) { update() }
+            }
+
+            override fun onUpdate() {
+                if (!mAllLightPackageOpsLiveData.isInitialized) {
+                    return
+                }
+
+                if (appPermGroupUiInfoLiveDataList.isEmpty()) {
+                    val appPermissionIds = mutableListOf<AppPermissionId>()
+                    val allPackages = mAllLightPackageOpsLiveData.value?.keys ?: setOf()
+                    for (packageWithUserHandle: Pair<String, UserHandle> in allPackages) {
+                        val lastPermissionGroupAccessTimesMs =
+                            mAllLightPackageOpsLiveData.value
+                                ?.get(packageWithUserHandle)
+                                ?.lastPermissionGroupAccessTimesMs
+                                ?: mapOf()
+
+                        for (permissionGroupToAccess in lastPermissionGroupAccessTimesMs) {
+                            appPermissionIds.add(
+                                AppPermissionId(
+                                    packageWithUserHandle.first,
+                                    packageWithUserHandle.second,
+                                    permissionGroupToAccess.key,
+                                ))
+                        }
+                    }
+
+                    setSourcesToDifference(
+                        appPermissionIds,
+                        appPermGroupUiInfoLiveDataList,
+                        getAppPermGroupUiInfoLiveData) {
+                            update()
+                        }
+                    appPermGroupListPopulated = true
+
+                    return
+                }
+
+                if (appPermGroupUiInfoLiveDataList.any { !it.value.isInitialized }) {
+                    return
+                }
+
+                if (isInitialized && appPermGroupUiInfoLiveDataList.any { it.value.isStale }) {
+                    return
+                }
+
+                val uiData = buildPermissionUsagesUiData()
+                // We include this check as we don't want UX updates unless the data to be displayed
+                // has changed. SmartUpdateMediatorLiveData sends updates if the data has changed OR
+                // if the data has changed from stale to fresh.
+                if (value != uiData) {
+                    value = uiData
+                }
+            }
+        }
+
+    /** Companion class for [PermissionUsageViewModelNew]. */
+    companion object {
+        private val TIME_7_DAYS_DURATION = TimeUnit.DAYS.toMillis(7)
+        private val TIME_24_HOURS_DURATION = TimeUnit.DAYS.toMillis(1)
+        internal const val SHOULD_SHOW_SYSTEM_KEY = "showSystem"
+        internal const val SHOULD_SHOW_7_DAYS_KEY = "show7Days"
+        private const val TELECOM_PACKAGE = "com.android.server.telecom"
+
+        /** Permission groups that should be hidden from the permissions usage UI. */
+        private val EXEMPTED_PERMISSION_GROUPS = setOf(Manifest.permission_group.NOTIFICATIONS)
+    }
+
+    /** Factory for [PermissionUsageViewModelNew]. */
+    @RequiresApi(Build.VERSION_CODES.S)
+    class PermissionUsageViewModelFactory(
+        val app: Application,
+        owner: SavedStateRegistryOwner,
+        defaultArgs: Bundle
+    ) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
+        override fun <T : ViewModel?> create(
+            key: String,
+            modelClass: Class<T>,
+            handle: SavedStateHandle
+        ): T {
+            @Suppress("UNCHECKED_CAST") return PermissionUsageViewModelNew(handle, app) as T
+        }
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/PermissionRationaleViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/PermissionRationaleViewModel.kt
new file mode 100644
index 0000000..eb9c5ba
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/PermissionRationaleViewModel.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.permission.ui.model.v34
+
+import android.app.Application
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.os.Process
+import android.util.Log
+import androidx.core.util.Consumer
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.permission.safetylabel.DataCategory
+import com.android.permission.safetylabel.DataType
+import com.android.permission.safetylabel.DataTypeConstants
+import com.android.permission.safetylabel.SafetyLabel
+import com.android.permissioncontroller.permission.data.SafetyLabelInfoLiveData
+import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
+import com.android.permissioncontroller.permission.data.get
+import com.android.permissioncontroller.permission.model.livedatatypes.SafetyLabelInfo.Companion.UNAVAILABLE
+import com.android.permissioncontroller.permission.utils.KotlinUtils
+import com.android.permissioncontroller.permission.utils.KotlinUtils.getAppStoreIntent
+import com.android.permissioncontroller.permission.utils.SafetyLabelPermissionMapping
+
+/**
+ * [ViewModel] for the [PermissionRationaleActivity]. Gets all information required safety label and
+ * links required to inform user of data sharing usages by the app when granting this permission
+ *
+ * @param app: The current application
+ * @param packageName: The packageName permissions are being requested for
+ * @param permissionGroupName: The permission group requested
+ * @param sessionId: A long to identify this session
+ * @param storedState: Previous state, if this activity was stopped and is being recreated
+ */
+class PermissionRationaleViewModel(
+    private val app: Application,
+    private val packageName: String,
+    private val permissionGroupName: String,
+    // TODO(b/259961958): add PermissionRationale metrics
+    private val sessionId: Long,
+    private val storedState: Bundle?
+) : ViewModel() {
+    private val user = Process.myUserHandle()
+    private val safetyLabelInfoLiveData = SafetyLabelInfoLiveData[packageName, user]
+
+    var activityResultCallback: Consumer<Intent>? = null
+
+    /**
+     * A class which represents a permission rationale for permission group, and messages which
+     * should be shown with it.
+     */
+    data class PermissionRationaleInfo(
+        val groupName: String,
+        val installSourcePackageName: String?,
+        val installSourceLabel: CharSequence?,
+        val purposeSet: Set<Int>
+    )
+
+    /** A [LiveData] which holds the currently pending PermissionRationaleInfo */
+    val permissionRationaleInfoLiveData =
+        object : SmartUpdateMediatorLiveData<PermissionRationaleInfo>() {
+
+            init {
+                addSource(safetyLabelInfoLiveData) { onUpdate() }
+
+                // Load package state, if available
+                onUpdate()
+            }
+
+            override fun onUpdate() {
+                if (safetyLabelInfoLiveData.isStale) {
+                    return
+                }
+
+                val safetyLabelInfo = safetyLabelInfoLiveData.value
+                val safetyLabel = safetyLabelInfo?.safetyLabel
+
+                if (safetyLabelInfo == null ||
+                    safetyLabelInfo == UNAVAILABLE ||
+                    safetyLabel == null) {
+                    Log.e(LOG_TAG, "Safety label for $packageName not found")
+                    value = null
+                    return
+                }
+
+                val installSourcePackageName = safetyLabelInfo.installSourcePackageName
+                val installSourceLabel: CharSequence? =
+                    installSourcePackageName?.let {
+                        KotlinUtils.getPackageLabel(app, it, Process.myUserHandle())
+                    }
+
+                value =
+                    PermissionRationaleInfo(
+                        permissionGroupName,
+                        installSourcePackageName,
+                        installSourceLabel,
+                        getSafetyLabelSharingPurposesForGroup(safetyLabel, permissionGroupName))
+            }
+
+            private fun getSafetyLabelSharingPurposesForGroup(
+                safetyLabel: SafetyLabel,
+                groupName: String
+            ): Set<Int> {
+                val purposeSet = mutableSetOf<Int>()
+                val categoriesForPermission: List<String> =
+                    SafetyLabelPermissionMapping.getCategoriesForPermissionGroup(groupName)
+                categoriesForPermission.forEach categoryLoop@{ category ->
+                    val dataCategory: DataCategory? = safetyLabel.dataLabel.dataShared[category]
+                    if (dataCategory == null) {
+                        // Continue to next
+                        return@categoryLoop
+                    }
+                    val typesForCategory = DataTypeConstants.getValidDataTypesForCategory(category)
+                    typesForCategory.forEach typeLoop@{ type ->
+                        val dataType: DataType? = dataCategory.dataTypes[type]
+                        if (dataType == null) {
+                            // Continue to next
+                            return@typeLoop
+                        }
+                        if (dataType.purposeSet.isNotEmpty()) {
+                            purposeSet.addAll(dataType.purposeSet)
+                        }
+                    }
+                }
+
+                return purposeSet
+            }
+        }
+
+    fun canLinkToAppStore(context: Context, installSourcePackageName: String): Boolean {
+        return getAppStoreIntent(context, installSourcePackageName, packageName) != null
+    }
+
+    fun sendToAppStore(context: Context, installSourcePackageName: String) {
+        val storeIntent = getAppStoreIntent(context, installSourcePackageName, packageName)
+        context.startActivity(storeIntent)
+    }
+
+    companion object {
+        private val LOG_TAG = PermissionRationaleViewModel::class.java.simpleName
+    }
+}
+
+/** Factory for a [PermissionRationaleViewModel] */
+class PermissionRationaleViewModelFactory(
+    private val app: Application,
+    private val packageName: String,
+    private val permissionGroupName: String,
+    private val sessionId: Long,
+    private val savedState: Bundle?
+) : ViewModelProvider.Factory {
+    override fun <T : ViewModel> create(modelClass: Class<T>): T {
+        @Suppress("UNCHECKED_CAST")
+        return PermissionRationaleViewModel(
+            app, packageName, permissionGroupName, sessionId, savedState)
+            as T
+    }
+}
diff --git a/service/java/com/android/permission/access/package-info.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/package-info.java
similarity index 73%
copy from service/java/com/android/permission/access/package-info.java
copy to PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/package-info.java
index af115be..4e3697c 100644
--- a/service/java/com/android/permission/access/package-info.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v34/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -14,9 +14,5 @@
  * limitations under the License.
  */
 
-/**
- * @hide
- * TODO(b/146466118) remove this javadoc tag
- */
-@android.annotation.Hide
-package com.android.permission.access;
+@androidx.annotation.RequiresApi(android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+package com.android.permissioncontroller.permission.ui.model.v34;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java
index 43c59b8..a5961dc 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java
@@ -34,7 +34,6 @@
 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME;
 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED;
 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT;
-import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack;
 
 import android.app.Activity;
 import android.app.AlertDialog;
@@ -272,7 +271,7 @@
 
     private void setRadioButtonsState(Map<ButtonType, ButtonState> states) {
         if (states == null && mViewModel.getButtonStateLiveData().isInitialized()) {
-            pressBack(this);
+            getFragmentManager().popBackStack();
             Log.w(LOG_TAG, "invalid package " + mPackageName + " or perm group "
                     + mPermGroupName);
             Toast.makeText(
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java
index 3c7a90b..45dd4c1 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java
@@ -146,8 +146,11 @@
 
     @Override
     public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon,
-            CharSequence message, CharSequence detailMessage, boolean[] buttonVisibilities,
+            CharSequence message, CharSequence detailMessage,
+            CharSequence permissionRationaleMessage, boolean[] buttonVisibilities,
             boolean[] locationVisibilities) {
+        // permissionRationaleMessage ignored by television
+
         // TODO: Handle detailMessage
 
         mGroupName = groupName;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleActivity.java
new file mode 100644
index 0000000..819291d
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleActivity.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.permission.ui.v34;
+
+import static android.content.Intent.EXTRA_PERMISSION_GROUP_NAME;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import static com.android.permission.safetylabel.DataPurposeConstants.PURPOSE_ACCOUNT_MANAGEMENT;
+import static com.android.permission.safetylabel.DataPurposeConstants.PURPOSE_ADVERTISING;
+import static com.android.permission.safetylabel.DataPurposeConstants.PURPOSE_ANALYTICS;
+import static com.android.permission.safetylabel.DataPurposeConstants.PURPOSE_APP_FUNCTIONALITY;
+import static com.android.permission.safetylabel.DataPurposeConstants.PURPOSE_DEVELOPER_COMMUNICATIONS;
+import static com.android.permission.safetylabel.DataPurposeConstants.PURPOSE_FRAUD_PREVENTION_SECURITY;
+import static com.android.permission.safetylabel.DataPurposeConstants.PURPOSE_PERSONALIZATION;
+import static com.android.permissioncontroller.permission.ui.v34.PermissionRationaleViewHandler.Result.CANCELLED;
+
+import android.content.Intent;
+import android.icu.lang.UCharacter;
+import android.icu.text.ListFormatter;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.Annotation;
+import android.text.SpannableStringBuilder;
+import android.text.style.ClickableSpan;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+import com.android.permission.safetylabel.DataPurposeConstants.Purpose;
+import com.android.permissioncontroller.Constants;
+import com.android.permissioncontroller.DeviceUtils;
+import com.android.permissioncontroller.R;
+import com.android.permissioncontroller.permission.ui.SettingsActivity;
+import com.android.permissioncontroller.permission.ui.handheld.v34.PermissionRationaleViewHandlerImpl;
+import com.android.permissioncontroller.permission.ui.model.v34.PermissionRationaleViewModel;
+import com.android.permissioncontroller.permission.ui.model.v34.PermissionRationaleViewModel.PermissionRationaleInfo;
+import com.android.permissioncontroller.permission.ui.model.v34.PermissionRationaleViewModelFactory;
+import com.android.permissioncontroller.permission.utils.KotlinUtils;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * An activity which displays runtime permission rationale on behalf of an app. This activity is
+ * based on GrantPermissionActivity to keep view behavior and theming consistent.
+ */
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+public class PermissionRationaleActivity extends SettingsActivity implements
+        PermissionRationaleViewHandler.ResultListener {
+
+    private static final String LOG_TAG = PermissionRationaleActivity.class.getSimpleName();
+
+    private static final String KEY_SESSION_ID = PermissionRationaleActivity.class.getName()
+            + "_SESSION_ID";
+
+    /**
+     * [Annotation] key for span annotations replacement within the permission rationale purposes
+     * string resource
+     */
+    public static final String ANNOTATION_ID_KEY = "id";
+    /**
+     * [Annotation] id value for span annotations replacement of link annotations within the
+     * permission rationale purposes string resource
+     */
+    public static final String LINK_ANNOTATION_ID = "link";
+    /**
+     * [Annotation] id value for span annotations replacement of install source annotations within
+     * the permission rationale purposes string resource
+     */
+    public static final String INSTALL_SOURCE_ANNOTATION_ID = "install_source";
+    /**
+     * [Annotation] id value for span annotations replacement of purpose list annotations within
+     * the permission rationale purposes string resource
+     */
+    public static final String PURPOSE_LIST_ANNOTATION_ID = "purpose_list";
+
+    /** Unique Id of a request. Inherited from GrantPermissionDialog if provide via intent extra */
+    private long mSessionId;
+    /** Package that shall have permissions granted */
+    private String mTargetPackage;
+    /** The permission group that initiated the permission rationale details activity */
+    private String mPermissionGroupName;
+    /** The permission rationale info resulting from the specified permission and group */
+    private PermissionRationaleInfo mPermissionRationaleInfo;
+
+    private PermissionRationaleViewHandler mViewHandler;
+    private PermissionRationaleViewModel mViewModel;
+
+    private float mOriginalDimAmount;
+    private View mRootView;
+
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        if (!KotlinUtils.INSTANCE.isPermissionRationaleEnabled()) {
+            Log.e(
+                    LOG_TAG,
+                    "Permission rationale feature disabled");
+            finishAfterTransition();
+            return;
+        }
+
+        if (icicle == null) {
+            mSessionId =
+                    getIntent().getLongExtra(Constants.EXTRA_SESSION_ID, new Random().nextLong());
+        } else {
+            mSessionId = icicle.getLong(KEY_SESSION_ID);
+        }
+
+        getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+
+        mPermissionGroupName = getIntent().getStringExtra(EXTRA_PERMISSION_GROUP_NAME);
+        if (mPermissionGroupName == null) {
+            Log.e(
+                    LOG_TAG,
+                    "null EXTRA_PERMISSION_GROUP_NAME. Must be set for permission rationale");
+            finishAfterTransition();
+            return;
+        }
+
+        mTargetPackage = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+        if (mTargetPackage == null) {
+            Log.e(LOG_TAG, "null EXTRA_PACKAGE_NAME. Must be set for permission rationale");
+            finishAfterTransition();
+            return;
+        }
+
+        setFinishOnTouchOutside(false);
+
+        setTitle(R.string.permission_rationale_title);
+
+        if (DeviceUtils.isTelevision(this)
+                || DeviceUtils.isWear(this)
+                || DeviceUtils.isAuto(this)) {
+            finishAfterTransition();
+        } else {
+            mViewHandler = new PermissionRationaleViewHandlerImpl(this, this);
+        }
+
+        PermissionRationaleViewModelFactory factory = new PermissionRationaleViewModelFactory(
+                getApplication(), mTargetPackage, mPermissionGroupName, mSessionId, icicle);
+        mViewModel = factory.create(PermissionRationaleViewModel.class);
+        mViewModel.getPermissionRationaleInfoLiveData()
+                .observe(this, this::onPermissionRationaleInfoLoad);
+
+        mRootView = mViewHandler.createView();
+        mRootView.setVisibility(View.GONE);
+        setContentView(mRootView);
+        Window window = getWindow();
+        WindowManager.LayoutParams layoutParams = window.getAttributes();
+        mOriginalDimAmount = layoutParams.dimAmount;
+        window.setAttributes(layoutParams);
+
+        if (getResources().getBoolean(R.bool.config_useWindowBlur)) {
+            java.util.function.Consumer<Boolean> blurEnabledListener = enabled -> {
+                mViewHandler.onBlurEnabledChanged(window, enabled);
+            };
+            mRootView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View v) {
+                    window.getWindowManager().addCrossWindowBlurEnabledListener(
+                            blurEnabledListener);
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View v) {
+                    window.getWindowManager().removeCrossWindowBlurEnabledListener(
+                            blurEnabledListener);
+                }
+            });
+        }
+        // Restore UI state after lifecycle events. This has to be before we show the first request,
+        // as the UI behaves differently for updates and initial creations.
+        if (icicle != null) {
+            mViewHandler.loadInstanceState(icicle);
+        } else {
+            // Do not show screen dim until data is loaded
+            window.setDimAmount(0f);
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(@NonNull Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        if (mViewHandler == null) {
+            return;
+        }
+
+        mViewHandler.saveInstanceState(outState);
+
+        outState.putLong(KEY_SESSION_ID, mSessionId);
+    }
+
+    @Override
+    public void onBackPressed() {
+        if (mViewHandler == null) {
+            return;
+        }
+        mViewHandler.onBackPressed();
+    }
+
+    // LINT.IfChange(dispatchTouchEvent)
+    /**
+     * Used to dismiss dialog when tapping outside of dialog bounds
+     * Follows the same logic as GrantPermissionActivity
+     */
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        View rootView = getWindow().getDecorView();
+        if (rootView.getTop() != 0) {
+            // We are animating the top view, need to compensate for that in motion events.
+            ev.setLocation(ev.getX(), ev.getY() - rootView.getTop());
+        }
+        final int x = (int) ev.getX();
+        final int y = (int) ev.getY();
+        if ((x < 0) || (y < 0) || (x > (rootView.getWidth())) || (y > (rootView.getHeight()))) {
+            if (MotionEvent.ACTION_DOWN == ev.getAction()) {
+                mViewHandler.onCancelled();
+            }
+            finishAfterTransition();
+        }
+        return super.dispatchTouchEvent(ev);
+    }
+    // LINT.ThenChange(GrantPermissionsActivity.java:dispatchTouchEvent)
+
+    @Override
+    public void onPermissionRationaleResult(@Nullable String groupName, int result) {
+        if (result == CANCELLED) {
+            finishAfterTransition();
+        }
+    }
+
+    private void onPermissionRationaleInfoLoad(PermissionRationaleInfo permissionRationaleInfo) {
+        if (permissionRationaleInfo == null) {
+            finishAfterTransition();
+            return;
+        }
+
+        mPermissionRationaleInfo = permissionRationaleInfo;
+        showPermissionRationale();
+    }
+
+    private void showPermissionRationale() {
+        List<String> purposesList =
+                new ArrayList<>(mPermissionRationaleInfo.getPurposeSet().size());
+        for (@Purpose int purpose : mPermissionRationaleInfo.getPurposeSet()) {
+            purposesList.add(getStringForPurpose(purpose));
+        }
+
+        // TODO(b/260144215): update purposes join based on l18n feedback
+        String purposesString = ListFormatter.getInstance().format(purposesList);
+
+        String installSourcePackageName = mPermissionRationaleInfo.getInstallSourcePackageName();
+        CharSequence installSourceLabel = mPermissionRationaleInfo.getInstallSourceLabel();
+        CharSequence purposeMessage;
+        if (installSourcePackageName == null || installSourcePackageName.length() == 0
+                || installSourceLabel == null || installSourceLabel.length() == 0) {
+            purposeMessage = getString(
+                    R.string.permission_rationale_purpose_default_source_message,
+                    purposesString);
+        } else {
+            purposeMessage =
+                    createPurposeMessageWithLink(
+                            getText(R.string.permission_rationale_purpose_message),
+                            installSourceLabel,
+                            purposesString,
+                            getLinkToAppStore(installSourcePackageName));
+        }
+
+        // TODO(b/260144330): link to permission settings
+        String groupName = mPermissionRationaleInfo.getGroupName();
+        String permissionGroupLabel =
+                KotlinUtils.INSTANCE.getPermGroupLabel(this, groupName).toString();
+        CharSequence settingsMessage =
+                getString(R.string.permission_rationale_permission_settings_message,
+                        UCharacter.toLowerCase(permissionGroupLabel));
+
+        // TODO(b/259963582): link to safety label help center article
+        CharSequence learnMoreMessage =
+                getString(R.string.permission_rationale_permission_learn_more_title);
+
+        mViewHandler.updateUi(
+                groupName,
+                purposeMessage,
+                settingsMessage,
+                learnMoreMessage
+        );
+
+        getWindow().setDimAmount(mOriginalDimAmount);
+        if (mRootView.getVisibility() == View.GONE) {
+            InputMethodManager manager = getSystemService(InputMethodManager.class);
+            manager.hideSoftInputFromWindow(mRootView.getWindowToken(), 0);
+            mRootView.setVisibility(View.VISIBLE);
+        }
+    }
+
+    private String getStringForPurpose(@Purpose int purpose) {
+        switch (purpose) {
+            case PURPOSE_APP_FUNCTIONALITY:
+                return getString(R.string.permission_rational_purpose_app_functionality);
+            case PURPOSE_ANALYTICS:
+                return getString(R.string.permission_rational_purpose_analytics);
+            case PURPOSE_DEVELOPER_COMMUNICATIONS:
+                return getString(R.string.permission_rational_purpose_developer_communications);
+            case PURPOSE_FRAUD_PREVENTION_SECURITY:
+                return getString(R.string.permission_rational_purpose_fraud_prevention_security);
+            case PURPOSE_ADVERTISING:
+                return getString(R.string.permission_rational_purpose_advertising);
+            case PURPOSE_PERSONALIZATION:
+                return getString(R.string.permission_rational_purpose_personalization);
+            case PURPOSE_ACCOUNT_MANAGEMENT:
+                return getString(R.string.permission_rational_purpose_account_management);
+            default:
+                throw new IllegalArgumentException("Invalid purpose: " + purpose);
+        }
+    }
+
+    private CharSequence createPurposeMessageWithLink(
+            CharSequence purposeText,
+            CharSequence installSourceLabel,
+            CharSequence purposes,
+            ClickableSpan link) {
+        SpannableStringBuilder text = SpannableStringBuilder.valueOf(purposeText);
+        Annotation[] annotations = text.getSpans(0, text.length(), Annotation.class);
+        // Sort the annotations in reverse order.
+        Arrays.sort(annotations, (a, b) -> text.getSpanStart(b) - text.getSpanStart(a));
+        SpannableStringBuilder messageWithSpan = new SpannableStringBuilder(text);
+        for (android.text.Annotation annotation : annotations) {
+            if (!annotation.getKey().equals(ANNOTATION_ID_KEY)) {
+                continue;
+            }
+
+            int spanStart = text.getSpanStart(annotation);
+            int spanEnd = text.getSpanEnd(annotation);
+            messageWithSpan.removeSpan(annotation);
+
+            switch (annotation.getValue()) {
+                case INSTALL_SOURCE_ANNOTATION_ID:
+                    messageWithSpan.replace(spanStart, spanEnd, installSourceLabel);
+                    break;
+                case LINK_ANNOTATION_ID:
+                    messageWithSpan.setSpan(link, spanStart, spanEnd, 0);
+                    break;
+                case PURPOSE_LIST_ANNOTATION_ID:
+                    messageWithSpan.replace(spanStart, spanEnd, purposes);
+                    break;
+                default:
+                    continue;
+            }
+        }
+        return messageWithSpan;
+    }
+
+    private ClickableSpan getLinkToAppStore(String installSourcePackageName) {
+        boolean canLinkToAppStore = mViewModel
+                .canLinkToAppStore(PermissionRationaleActivity.this, installSourcePackageName);
+        if (!canLinkToAppStore) {
+            return null;
+        }
+        return new ClickableSpan() {
+            @Override
+            public void onClick(@NonNull View widget) {
+                // TODO(b/259961958): metrics for click events
+                mViewModel.sendToAppStore(PermissionRationaleActivity.this,
+                        installSourcePackageName);
+            }
+        };
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleViewHandler.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleViewHandler.kt
new file mode 100644
index 0000000..7b22959
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/PermissionRationaleViewHandler.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.permission.ui.v34
+
+import android.os.Build
+import android.os.Bundle
+import android.view.View
+import android.view.Window
+import androidx.annotation.IntDef
+import androidx.annotation.RequiresApi
+
+/**
+ * Class for managing the presentation and user interaction of the "permission rationale" user
+ * interface.
+ */
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+interface PermissionRationaleViewHandler {
+    @Retention(AnnotationRetention.SOURCE)
+    @IntDef(Result.CANCELLED)
+    annotation class Result {
+        companion object {
+            const val CANCELLED = -1
+        }
+    }
+
+    /**
+     * Listener interface for getting notified when the user responds to a permission rationale
+     * user action.
+     */
+    interface ResultListener {
+        fun onPermissionRationaleResult(groupName: String?, @Result result: Int)
+    }
+
+    /**
+     * Creates and returns the view hierarchy that is managed by this view handler. This must be
+     * called before [.updateUi].
+     */
+    fun createView(): View
+
+    /**
+     * Updates the view hierarchy to reflect the specified state.
+     *
+     * Note that this must be called at least once before showing the UI to the user to properly
+     * initialize the UI.
+     *
+     * @param groupName the name of the permission group
+     * @param purposeMessage the data usage purposes message to display the user
+     * @param settingsMessage the settings link message to display the user
+     * @param learnMoreMessage the more info about safety labels message to display the user
+     */
+    fun updateUi(
+        groupName: String,
+        purposeMessage: CharSequence,
+        settingsMessage: CharSequence,
+        learnMoreMessage: CharSequence
+    )
+
+    /**
+     * Called by [PermissionRationaleActivity] to save the state of this view handler to the
+     * specified bundle.
+     */
+    fun saveInstanceState(outState: Bundle)
+
+    /**
+     * Called by [PermissionRationaleActivity] to load the state of this view handler from the
+     * specified bundle.
+     */
+    fun loadInstanceState(savedInstanceState: Bundle)
+
+    /** Gives a chance for handling the back key. */
+    fun onBackPressed()
+
+    /**
+     * Handles cancel event for the permission rationale dialog.
+     */
+    fun onCancelled() {}
+
+    /**
+     * Called by [PermissionRationaleActivity] to allow the handler to update the ui when blur is
+     * enabled/disabled.
+     */
+    fun onBlurEnabledChanged(window: Window?, enabled: Boolean) {}
+}
diff --git a/service/java/com/android/permission/access/package-info.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/package-info.java
similarity index 74%
rename from service/java/com/android/permission/access/package-info.java
rename to PermissionController/src/com/android/permissioncontroller/permission/ui/v34/package-info.java
index af115be..0e68b52 100644
--- a/service/java/com/android/permission/access/package-info.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/v34/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -14,9 +14,5 @@
  * limitations under the License.
  */
 
-/**
- * @hide
- * TODO(b/146466118) remove this javadoc tag
- */
-@android.annotation.Hide
-package com.android.permission.access;
+@androidx.annotation.RequiresApi(android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+package com.android.permissioncontroller.permission.ui.v34;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java
index 1c0af63..ccd1ed4 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java
@@ -96,8 +96,11 @@
 
     @Override
     public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon,
-            CharSequence message, CharSequence detailMessage, boolean[] buttonVisibilities,
+            CharSequence message, CharSequence detailMessage,
+            CharSequence permissionRationaleMessage, boolean[] buttonVisibilities,
             boolean[] locationVisibilities) {
+        // permissionRationaleMessage ignored by wear
+
         // TODO: Handle detailMessage
 
         boolean showDoNotAsk = buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON];
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
index 82ad249..fad90fc 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
@@ -21,6 +21,8 @@
 import android.Manifest.permission.ACCESS_FINE_LOCATION
 import android.Manifest.permission.BACKUP
 import android.Manifest.permission.POST_NOTIFICATIONS
+import android.Manifest.permission.READ_MEDIA_IMAGES
+import android.Manifest.permission.READ_MEDIA_VIDEO
 import android.Manifest.permission_group.NOTIFICATIONS
 import android.app.ActivityManager
 import android.app.AppOpsManager
@@ -46,10 +48,12 @@
 import android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE
 import android.content.pm.PermissionGroupInfo
 import android.content.pm.PermissionInfo
+import android.content.pm.ResolveInfo
 import android.content.res.Resources
 import android.graphics.Bitmap
 import android.graphics.Canvas
 import android.graphics.drawable.Drawable
+import android.healthconnect.HealthConnectManager
 import android.os.Build
 import android.os.Bundle
 import android.os.UserHandle
@@ -75,16 +79,17 @@
 import com.android.permissioncontroller.permission.model.livedatatypes.PermState
 import com.android.permissioncontroller.permission.service.LocationAccessCheck
 import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.async
-import kotlinx.coroutines.launch
 import java.util.concurrent.atomic.AtomicReference
 import kotlin.coroutines.Continuation
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.resume
 import kotlin.coroutines.suspendCoroutine
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
+
 /**
  * A set of util functions designed to work with kotlin, though they can work with java, as well.
  */
@@ -117,6 +122,158 @@
     private val ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE =
         ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
 
+    /** Whether to show the Permissions Hub.  */
+    private const val PROPERTY_PERMISSIONS_HUB_2_ENABLED = "permissions_hub_2_enabled"
+
+    /** Whether to show the mic and camera icons.  */
+    private const val PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled"
+
+    /** Whether to show the location indicators. */
+    private const val PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled"
+
+    /** Whether location accuracy feature is enabled */
+    private const val PROPERTY_LOCATION_ACCURACY_ENABLED = "location_accuracy_enabled"
+
+    /** Whether to show 7-day toggle in privacy hub.  */
+    private const val PRIVACY_DASHBOARD_7_DAY_TOGGLE = "privacy_dashboard_7_day_toggle"
+
+    /** Whether to show permission rationale in permission settings and grant dialog.  */
+    private const val PRIVACY_PERMISSION_RATIONALE_ENABLED = "privacy_permission_rationale_enabled"
+
+    /** Whether to placeholder safety label data in permission settings and grant dialog.  */
+    private const val PRIVACY_PLACEHOLDER_SAFETY_LABEL_DATA_ENABLED =
+        "privacy_placeholder_safety_label_data_enabled"
+
+    /** Default location precision */
+    private const val PROPERTY_LOCATION_PRECISION = "location_precision"
+
+    /** Whether to show the photo picker option in permission prompts.  */
+    private const val PROPERTY_PHOTO_PICKER_PROMPT_ENABLED = "photo_picker_prompt_enabled"
+
+    /**
+     * Whether the Permissions Hub 2 flag is enabled
+     *
+     * @return whether the flag is enabled
+     */
+    @ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
+    fun isPermissionsHub2FlagEnabled(): Boolean {
+        return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+            PROPERTY_PERMISSIONS_HUB_2_ENABLED, false)
+    }
+    /**
+     * Whether to show the Permissions Dashboard
+     *
+     * @return whether to show the Permissions Dashboard.
+     */
+    @ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
+    fun shouldShowPermissionsDashboard(): Boolean {
+        return isPermissionsHub2FlagEnabled()
+    }
+
+    /**
+     * Whether the Camera and Mic Icons are enabled by flag.
+     *
+     * @return whether the Camera and Mic Icons are enabled.
+     */
+    @ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
+    fun isCameraMicIconsFlagEnabled(): Boolean {
+        return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+            PROPERTY_CAMERA_MIC_ICONS_ENABLED, true)
+    }
+
+    /**
+     * Whether to show Camera and Mic Icons. They should be shown if the permission hub, or the icons
+     * specifically, are enabled.
+     *
+     * @return whether to show the icons.
+     */
+    @ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
+    fun shouldShowCameraMicIndicators(): Boolean {
+        return isCameraMicIconsFlagEnabled() || isPermissionsHub2FlagEnabled()
+    }
+
+    /**
+     * Whether the location indicators are enabled by flag.
+     *
+     * @return whether the location indicators are enabled by flag.
+     */
+    @ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
+    fun isLocationIndicatorsFlagEnabled(): Boolean {
+        return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+            PROPERTY_LOCATION_INDICATORS_ENABLED, false)
+    }
+
+    /**
+     * Whether to show the location indicators. The location indicators are enable if the
+     * permission hub, or location indicator specifically are enabled.
+     */
+    @ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
+    fun shouldShowLocationIndicators(): Boolean {
+        return isLocationIndicatorsFlagEnabled() || isPermissionsHub2FlagEnabled()
+    }
+
+    /**
+     * Whether the location accuracy feature is enabled
+     */
+    @ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
+    fun isLocationAccuracyEnabled(): Boolean {
+        return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+            PROPERTY_LOCATION_ACCURACY_ENABLED, true)
+    }
+
+    /**
+     * Default state of location precision
+     * true: default is FINE.
+     * false: default is COARSE.
+     */
+    fun getDefaultPrecision(): Boolean {
+        return !SdkLevel.isAtLeastS() || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+            PROPERTY_LOCATION_PRECISION, true)
+    }
+
+    /**
+     * Whether we should enable the 7-day toggle in privacy dashboard
+     *
+     * @return whether the flag is enabled
+     */
+    @ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
+    fun is7DayToggleEnabled(): Boolean {
+        return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+            PRIVACY_DASHBOARD_7_DAY_TOGGLE, false)
+    }
+
+    /**
+     * Whether the Photo Picker Prompt is enabled
+     *
+     * @return `true` iff the Location Access Check is enabled.
+     */
+    @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake")
+    fun isPhotoPickerPromptEnabled(): Boolean {
+        return SdkLevel.isAtLeastU() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+            PROPERTY_PHOTO_PICKER_PROMPT_ENABLED, false)
+    }
+
+    /*
+     * Whether we should enable the permission rationale in permission settings and grant dialog
+     *
+     * @return whether the flag is enabled
+     */
+    @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake")
+    fun isPermissionRationaleEnabled(): Boolean {
+        return SdkLevel.isAtLeastU() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+            PRIVACY_PERMISSION_RATIONALE_ENABLED, false)
+    }
+
+    /**
+     * Whether we should use placeholder safety label data
+     *
+     * @return whether the flag is enabled
+     */
+    fun isPlaceholderSafetyLabelDataEnabled(): Boolean {
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+            PRIVACY_PLACEHOLDER_SAFETY_LABEL_DATA_ENABLED, false)
+    }
+
     /**
      * Given a Map, and a List, determines which elements are in the list, but not the map, and
      * vice versa. Used primarily for determining which liveDatas are already being watched, and
@@ -385,6 +542,20 @@
     }
 
     /**
+     * Return a specific MIME type, if a set of permissions is associated with one
+     */
+    fun getMimeTypeForPermissions(permissions: List<String>): String? {
+        if (permissions.contains(READ_MEDIA_IMAGES) && !permissions.contains(READ_MEDIA_VIDEO)) {
+            return "image/*"
+        }
+        if (permissions.contains(READ_MEDIA_VIDEO) && !permissions.contains(READ_MEDIA_IMAGES)) {
+            return "video/*"
+        }
+
+        return null
+    }
+
+    /**
      * Determines if an app is R or above, or if it is Q-, and has auto revoke enabled
      *
      * @param app The currenct application
@@ -494,9 +665,12 @@
         app: Application,
         group: LightAppPermGroup,
         filterPermissions: List<String> = group.permissions.keys.toList(),
-        isOneTime: Boolean = false
+        isOneTime: Boolean = false,
+        userFixed: Boolean = false,
+        withoutAppOps: Boolean = false,
     ): LightAppPermGroup {
-        return grantRuntimePermissions(app, group, false, isOneTime, filterPermissions)
+        return grantRuntimePermissions(app, group, false, isOneTime, userFixed,
+            withoutAppOps, filterPermissions)
     }
 
     /**
@@ -518,7 +692,8 @@
         group: LightAppPermGroup,
         filterPermissions: List<String> = group.permissions.keys.toList()
     ): LightAppPermGroup {
-        return grantRuntimePermissions(app, group, true, false, filterPermissions)
+        return grantRuntimePermissions(app, group, true, false, false, false,
+            filterPermissions)
     }
 
     private fun grantRuntimePermissions(
@@ -526,16 +701,18 @@
         group: LightAppPermGroup,
         grantBackground: Boolean,
         isOneTime: Boolean = false,
-        filterPermissions: List<String> = group.permissions.keys.toList()
+        userFixed: Boolean = false,
+        withoutAppOps: Boolean = false,
+        filterPermissions: List<String> = group.permissions.keys.toList(),
     ): LightAppPermGroup {
-        val wasOneTime = group.isOneTime
         val newPerms = group.permissions.toMutableMap()
         var shouldKillForAnyPermission = false
         for (permName in filterPermissions) {
             val perm = group.permissions[permName] ?: continue
             val isBackgroundPerm = permName in group.backgroundPermNames
             if (isBackgroundPerm == grantBackground) {
-                val (newPerm, shouldKill) = grantRuntimePermission(app, perm, isOneTime, group)
+                val (newPerm, shouldKill) = grantRuntimePermission(app, perm, group, isOneTime,
+                    userFixed, withoutAppOps)
                 newPerms[newPerm.name] = newPerm
                 shouldKillForAnyPermission = shouldKillForAnyPermission || shouldKill
             }
@@ -544,7 +721,7 @@
             val user = UserHandle.getUserHandleForUid(group.packageInfo.uid)
             for (groupPerm in group.allPermissions.values) {
                 var permFlags = groupPerm!!.flags
-                permFlags = permFlags.clearFlag(PackageManager.FLAG_PERMISSION_AUTO_REVOKED)
+                permFlags = permFlags.clearFlag(FLAG_PERMISSION_AUTO_REVOKED)
                 if (groupPerm!!.flags != permFlags) {
                     app.packageManager.updatePermissionFlags(groupPerm!!.name,
                         group.packageInfo.packageName, PERMISSION_CONTROLLER_CHANGED_FLAG_MASK,
@@ -582,8 +759,12 @@
      *
      * @param app The current application
      * @param perm The permission which should be granted.
-     * @param group An optional app permission group in which to look for background or foreground
+     * @param group An app permission group in which to look for background or foreground
+     * @param isOneTime Whether this is a one-time permission grant
      * permissions
+     * @param userFixed Whether to mark the permissions as user fixed when granted
+     * @param withoutAppOps If these permission have app ops associated, and this value is true,
+     * then do not grant the app op when the permission is granted, and add the REVOKED_COMPAT flag.
      *
      * @return a LightPermission and boolean pair <permission with updated state (or the original
      * state, if it wasn't changed), should kill app>
@@ -591,8 +772,10 @@
     private fun grantRuntimePermission(
         app: Application,
         perm: LightPermission,
+        group: LightAppPermGroup,
         isOneTime: Boolean,
-        group: LightAppPermGroup
+        userFixed: Boolean = false,
+        withoutAppOps: Boolean = false
     ): Pair<LightPermission, Boolean> {
         val pkgInfo = group.packageInfo
         val user = UserHandle.getUserHandleForUid(pkgInfo.uid)
@@ -624,31 +807,39 @@
                 shouldKill = true
                 isGranted = true
             }
-            newFlags = newFlags.clearFlag(PackageManager.FLAG_PERMISSION_REVOKED_COMPAT)
+            newFlags = if (withoutAppOps) {
+                newFlags.setFlag(PackageManager.FLAG_PERMISSION_REVOKED_COMPAT)
+            } else {
+                newFlags.clearFlag(PackageManager.FLAG_PERMISSION_REVOKED_COMPAT)
+            }
             newFlags = newFlags.clearFlag(PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED)
 
             // If this permission affects an app op, ensure the permission app op is enabled
             // before the permission grant.
-            if (affectsAppOp) {
+            if (affectsAppOp && !withoutAppOps) {
                 allowAppOp(app, perm, group)
             }
         }
 
         // Granting a permission explicitly means the user already
         // reviewed it so clear the review flag on every grant.
-        newFlags = newFlags.clearFlag(PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED)
+        newFlags = newFlags.clearFlag(FLAG_PERMISSION_REVIEW_REQUIRED)
 
         // Update the permission flags
-        // Now the apps can ask for the permission as the user
-        // no longer has it fixed in a denied state.
-        newFlags = newFlags.clearFlag(PackageManager.FLAG_PERMISSION_USER_FIXED)
-        newFlags = newFlags.setFlag(PackageManager.FLAG_PERMISSION_USER_SET)
-        newFlags = newFlags.clearFlag(PackageManager.FLAG_PERMISSION_AUTO_REVOKED)
+        if (!withoutAppOps && !userFixed) {
+            // Now the apps can ask for the permission as the user
+            // no longer has it fixed in a denied state.
+            newFlags = newFlags.clearFlag(FLAG_PERMISSION_USER_FIXED)
+            newFlags = newFlags.setFlag(FLAG_PERMISSION_USER_SET)
+        } else if (userFixed) {
+            newFlags = newFlags.setFlag(FLAG_PERMISSION_USER_FIXED)
+        }
+        newFlags = newFlags.clearFlag(FLAG_PERMISSION_AUTO_REVOKED)
 
         newFlags = if (isOneTime) {
-            newFlags.setFlag(PackageManager.FLAG_PERMISSION_ONE_TIME)
+            newFlags.setFlag(FLAG_PERMISSION_ONE_TIME)
         } else {
-            newFlags.clearFlag(PackageManager.FLAG_PERMISSION_ONE_TIME)
+            newFlags.clearFlag(FLAG_PERMISSION_ONE_TIME)
         }
 
         // If we newly grant background access to the fine location, double-guess the user some
@@ -1196,6 +1387,44 @@
             context.getDrawable(android.R.drawable.ic_safety_protection) != null &&
             !context.getString(android.R.string.safety_protection_display_text).isNullOrEmpty()
     }
+
+    fun addHealthPermissions(context: Context) {
+        val permissions = HealthConnectManager.getHealthPermissions(context)
+        PermissionMapping.addHealthPermissionsToPlatform(permissions)
+    }
+
+    /**
+     * Returns an [Intent] to the installer app store for a given package name, or {@code null} if
+     * none found
+     */
+    fun getAppStoreIntent(
+        context: Context,
+        installerPackageName: String?,
+        packageName: String?
+    ): Intent? {
+        val intent: Intent = Intent(Intent.ACTION_SHOW_APP_INFO)
+            .setPackage(installerPackageName)
+        val result: Intent? = resolveActivityForIntent(context, intent)
+        if (result != null) {
+            result.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
+            return result
+        }
+        return null
+    }
+
+    /**
+     * Verify that a component that supports the intent with action and return a new intent with
+     * same action and resolved class name set. Returns null if no activity resolution.
+     */
+    private fun resolveActivityForIntent(context: Context, intent: Intent): Intent? {
+        val result: ResolveInfo? = context.packageManager.resolveActivity(intent, 0)
+        return if (result != null) {
+            Intent(intent.action)
+                .setClassName(result.activityInfo.packageName, result.activityInfo.name)
+        } else {
+            null
+        }
+    }
 }
 
 /**
@@ -1267,4 +1496,4 @@
             navigate(destResId, args)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt
index fa7cbec..4e70e1b 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt
@@ -19,6 +19,8 @@
 import android.Manifest
 import android.content.pm.PackageManager
 import android.content.pm.PermissionInfo
+import android.healthconnect.HealthPermissions.HEALTH_PERMISSION_GROUP
+import android.util.Log
 import com.android.modules.utils.build.SdkLevel
 
 /**
@@ -27,6 +29,8 @@
  */
 object PermissionMapping {
 
+    private val LOG_TAG = "PermissionMapping"
+
     @JvmField
     val SENSOR_DATA_PERMISSIONS: List<String> =
         listOf(
@@ -52,6 +56,8 @@
     /** Set of groups that will be able to receive one-time grant */
     private val ONE_TIME_PERMISSION_GROUPS: MutableSet<String> = mutableSetOf()
 
+    private val HEALTH_PERMISSIONS_SET: MutableSet<String> = mutableSetOf()
+
     init {
         PLATFORM_PERMISSIONS[Manifest.permission.READ_CONTACTS] = Manifest.permission_group.CONTACTS
         PLATFORM_PERMISSIONS[Manifest.permission.WRITE_CONTACTS] =
@@ -62,6 +68,9 @@
         PLATFORM_PERMISSIONS[Manifest.permission.WRITE_CALENDAR] =
             Manifest.permission_group.CALENDAR
 
+        // Any updates to the permissions for the SMS permission group must also be made in
+        // Permissions {@link com.android.role.controller.model.Permissions} in the role
+        // library
         PLATFORM_PERMISSIONS[Manifest.permission.SEND_SMS] = Manifest.permission_group.SMS
         PLATFORM_PERMISSIONS[Manifest.permission.RECEIVE_SMS] = Manifest.permission_group.SMS
         PLATFORM_PERMISSIONS[Manifest.permission.READ_SMS] = Manifest.permission_group.SMS
@@ -92,6 +101,11 @@
                 Manifest.permission_group.READ_MEDIA_VISUAL
         }
 
+        if (SdkLevel.isAtLeastU()) {
+            PLATFORM_PERMISSIONS[Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED] =
+                Manifest.permission_group.READ_MEDIA_VISUAL
+        }
+
         PLATFORM_PERMISSIONS[Manifest.permission.ACCESS_FINE_LOCATION] =
             Manifest.permission_group.LOCATION
         PLATFORM_PERMISSIONS[Manifest.permission.ACCESS_COARSE_LOCATION] =
@@ -114,6 +128,9 @@
                 Manifest.permission_group.NEARBY_DEVICES
         }
 
+        // Any updates to the permissions for the CALL_LOG permission group must also be made in
+        // Permissions {@link com.android.role.controller.model.Permissions} in the role
+        // library
         PLATFORM_PERMISSIONS[Manifest.permission.READ_CALL_LOG] = Manifest.permission_group.CALL_LOG
         PLATFORM_PERMISSIONS[Manifest.permission.WRITE_CALL_LOG] =
             Manifest.permission_group.CALL_LOG
@@ -274,4 +291,31 @@
     fun supportsOneTimeGrant(permissionGroup: String?): Boolean {
         return ONE_TIME_PERMISSION_GROUPS.contains(permissionGroup)
     }
+
+    /**
+     * Adds health permissions as platform permissions.
+     */
+    @JvmStatic
+    fun addHealthPermissionsToPlatform(permissions: Set<String>) {
+        if (permissions.isEmpty()) {
+            Log.w(LOG_TAG, "No health connect permissions found.")
+            return
+        }
+
+        PLATFORM_PERMISSION_GROUPS[HEALTH_PERMISSION_GROUP] = mutableListOf()
+
+        for (permission in permissions) {
+            PLATFORM_PERMISSIONS[permission] = HEALTH_PERMISSION_GROUP
+            PLATFORM_PERMISSION_GROUPS[HEALTH_PERMISSION_GROUP]?.add(permission)
+            HEALTH_PERMISSIONS_SET.add(permission)
+        }
+    }
+
+    /**
+     * Returns true if the given permission is a health platform permission.
+     */
+    @JvmStatic
+    fun isHealthPermission(permissionName: String): Boolean {
+        return HEALTH_PERMISSIONS_SET.contains(permissionName)
+    }
 }
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/SafetyLabelPermissionMapping.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/SafetyLabelPermissionMapping.kt
new file mode 100644
index 0000000..5fd852b
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/SafetyLabelPermissionMapping.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.permission.utils
+
+import android.Manifest
+import com.android.permission.safetylabel.DataCategoryConstants
+
+/**
+ * This file contains the canonical mapping of permission and permission group to Safety Label
+ * categories and types used in the Permission settings screens and grant dialog. It also includes
+ * methods related to that mapping.
+ */
+object SafetyLabelPermissionMapping {
+
+    /**
+     * Get the Safety Label categories pertaining to a specified permission group.
+     *
+     * @param groupName the permission group name
+     *
+     * @return The categories or an empty list if the group does not have supported and mapped group
+     * to safety label category
+     */
+    fun getCategoriesForPermissionGroup(groupName: String): List<String> {
+        return if (groupName == Manifest.permission_group.LOCATION) {
+            listOf(DataCategoryConstants.CATEGORY_LOCATION)
+        } else {
+            emptyList()
+        }
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java b/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java
index 8f5a70c..ef03673 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/Utils.java
@@ -34,12 +34,15 @@
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.OPSTR_LEGACY_STORAGE;
 import static android.content.Context.MODE_PRIVATE;
+import static android.content.Intent.EXTRA_PACKAGE_NAME;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static android.healthconnect.HealthConnectManager.ACTION_MANAGE_HEALTH_PERMISSIONS;
+import static android.healthconnect.HealthConnectManager.ACTION_MANAGE_HEALTH_PERMISSIONS_AND_DATA;
 import static android.os.UserHandle.myUserId;
 
 import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
@@ -87,6 +90,7 @@
 import android.view.Menu;
 import android.view.MenuItem;
 
+import androidx.annotation.ChecksSdkIntAtLeast;
 import androidx.annotation.ColorRes;
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
@@ -168,6 +172,11 @@
     private static final String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED =
             "location_access_check_enabled";
 
+    /** Whether to show health permission in various permission controller UIs. */
+    private static final String PROPERTY_HEALTH_PERMISSION_UI_ENABLED =
+            "health_permission_ui_enabled";
+
+
     /** How frequently to check permission event store to scrub old data */
     public static final String PROPERTY_PERMISSION_EVENTS_CHECK_OLD_FREQUENCY_MILLIS =
             "permission_events_check_old_frequency_millis";
@@ -923,6 +932,16 @@
     }
 
     /**
+     * Whether we should show health permissions as platform permissions in the various
+     * permission controller UI.
+     */
+    @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake")
+    public static boolean isHealthPermissionUiEnabled() {
+        return SdkLevel.isAtLeastU() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                PROPERTY_HEALTH_PERMISSION_UI_ENABLED, true);
+    }
+
+    /**
      * Get a device protected storage based shared preferences. Avoid storing sensitive data in it.
      *
      * @param context the context to get the shared preferences
@@ -1236,6 +1255,28 @@
     }
 
     /**
+     * Navigate to health connect settings for all apps
+     * @param context The current Context
+     */
+    public static void navigateToHealthConnectSettings(@NonNull Context context) {
+        Intent healthConnectIntent = new Intent(ACTION_MANAGE_HEALTH_PERMISSIONS);
+        context.startActivity(healthConnectIntent);
+    }
+
+    /**
+     * Navigate to health connect settings for an app
+     * @param context The current Context
+     * @param packageName The package's health connect settings to navigate to
+     */
+    public static void navigateToAppHealthConnectSettings(@NonNull Context context,
+            @NonNull String packageName, @NonNull UserHandle user) {
+        Intent appHealthConnectIntent = new Intent(ACTION_MANAGE_HEALTH_PERMISSIONS_AND_DATA);
+        appHealthConnectIntent.putExtra(EXTRA_PACKAGE_NAME, packageName);
+        appHealthConnectIntent.putExtra(Intent.EXTRA_USER, user);
+        context.startActivity(appHealthConnectIntent);
+    }
+
+    /**
      * Returns if a card should be shown if the sensor is blocked
      **/
     public static boolean shouldDisplayCardIfBlocked(@NonNull String permissionGroupName) {
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySettingsUtil.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySettingsUtil.kt
index be3f103..59dbf63 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySettingsUtil.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySettingsUtil.kt
@@ -62,7 +62,7 @@
     /**
      * @return the mutable set of enabled accessibility services.
      */
-    private fun getEnabledServicesFromSettings(context: Context): MutableSet<ComponentName> {
+    fun getEnabledServicesFromSettings(context: Context): MutableSet<ComponentName> {
         val enabledServicesSetting = Settings.Secure.getString(
             context.contentResolver, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
         )
@@ -82,6 +82,8 @@
             )
             if (enabledService != null) {
                 enabledServices.add(enabledService)
+            } else {
+                Log.e(LOG_TAG, "unable to parse accessibility service $componentNameString")
             }
         }
 
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt
index a1a4873..3fc03e8 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt
@@ -17,7 +17,6 @@
 package com.android.permissioncontroller.privacysources
 
 import android.accessibilityservice.AccessibilityServiceInfo
-import android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK
 import android.app.Notification
 import android.app.NotificationChannel
 import android.app.NotificationManager
@@ -501,10 +500,19 @@
      * @return enabled 3rd party accessibility services.
      */
     fun getEnabledAccessibilityServices(): List<AccessibilityServiceInfo> {
-        return accessibilityManager.getEnabledAccessibilityServiceList(
-            FEEDBACK_ALL_MASK
-        ).filter { !it.isAccessibilityTool }
-            .filter { ComponentName.unflattenFromString(it.id) != null }
+        val installedServices = accessibilityManager.getInstalledAccessibilityServiceList()
+            .associateBy { ComponentName.unflattenFromString(it.id) }
+        val enabledServices = AccessibilitySettingsUtil.getEnabledServicesFromSettings(context)
+            .map {
+                if (installedServices[it] == null) {
+                    Log.e(LOG_TAG, "enabled accessibility service ($it) not found in installed" +
+                        "services: ${installedServices.keys}")
+                }
+                installedServices[it]
+            }
+
+        return enabledServices.filterNotNull()
+            .filter { !it.isAccessibilityTool }
     }
 
     /**
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt
index 5639006..4e2228d 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/NotificationListenerCheck.kt
@@ -90,8 +90,7 @@
 import kotlinx.coroutines.sync.withLock
 
 private val TAG = "NotificationListenerCheck"
-private const val DEBUG = false
-
+private const val DEBUG = true
 const val SC_NLS_SOURCE_ID = "AndroidNotificationListener"
 @VisibleForTesting const val SC_NLS_DISABLE_ACTION_ID = "disable_nls_component"
 
@@ -840,7 +839,7 @@
 
     override fun onCreate() {
         super.onCreate()
-
+        if (DEBUG) Log.d(TAG, "Nls privacy job created")
         if (!checkNotificationListenerCheckEnabled(this)) {
             // NotificationListenerCheck not enabled. End job.
             return
@@ -865,6 +864,7 @@
      * @return `false` if another check is already running, or if SDK Check fails (below T)
      */
     override fun onStartJob(params: JobParameters): Boolean {
+        if (DEBUG) Log.d(TAG, "Nls privacy job started")
         if (!checkNotificationListenerCheckEnabled(this)) {
             // NotificationListenerCheck not enabled. End job.
             return false
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/SafetyCenterReceiver.kt b/PermissionController/src/com/android/permissioncontroller/privacysources/SafetyCenterReceiver.kt
index 9254aab..89f197a 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/SafetyCenterReceiver.kt
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/SafetyCenterReceiver.kt
@@ -76,11 +76,6 @@
                 PermissionControllerApplication.get().applicationContext,
                 SafetyCenterManager::class.java)
 
-        if (!safetyCenterManager.isSafetyCenterEnabled &&
-            intent.action != ACTION_SAFETY_CENTER_ENABLED_CHANGED) {
-            return
-        }
-
         val mapOfSourceIdsToSources = getMapOfSourceIdsToSources(context)
 
         when (intent.action) {
@@ -91,24 +86,30 @@
                     mapOfSourceIdsToSources.values)
             }
             ACTION_REFRESH_SAFETY_SOURCES -> {
-                val sourceIdsExtra = intent.getStringArrayExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS)
-                if (sourceIdsExtra != null && sourceIdsExtra.isNotEmpty()) {
+                if (safetyCenterManager.isSafetyCenterEnabled) {
+                    val sourceIdsExtra = intent.getStringArrayExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS)
+                    if (sourceIdsExtra != null && sourceIdsExtra.isNotEmpty()) {
+                        refreshSafetySources(
+                            context,
+                            intent,
+                            RefreshEvent.EVENT_REFRESH_REQUESTED,
+                            mapOfSourceIdsToSources,
+                            sourceIdsExtra.toList())
+                    }
+                }
+            }
+
+            ACTION_BOOT_COMPLETED -> {
+                updateTileVisibility(context, safetyCenterManager.isSafetyCenterEnabled)
+                if (safetyCenterManager.isSafetyCenterEnabled) {
                     refreshSafetySources(
                         context,
                         intent,
-                        RefreshEvent.EVENT_REFRESH_REQUESTED,
+                        RefreshEvent.EVENT_DEVICE_REBOOTED,
                         mapOfSourceIdsToSources,
-                        sourceIdsExtra.toList())
+                        mapOfSourceIdsToSources.keys.toList())
                 }
             }
-            ACTION_BOOT_COMPLETED -> {
-                refreshSafetySources(
-                    context,
-                    intent,
-                    RefreshEvent.EVENT_DEVICE_REBOOTED,
-                    mapOfSourceIdsToSources,
-                    mapOfSourceIdsToSources.keys.toList())
-            }
         }
     }
 
diff --git a/PermissionController/src/com/android/permissioncontroller/privacysources/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/privacysources/TEST_MAPPING
index 921ef7d..3d2d5f8 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/TEST_MAPPING
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/TEST_MAPPING
@@ -26,6 +26,14 @@
       ]
     },
     {
+      "name": "SafetyCenterFunctionalTestCases",
+      "options": [
+        {
+          "exclude-annotation": "com.android.bedstead.harrier.annotations.Postsubmit"
+        }
+      ]
+    },
+    {
       "name": "CtsPermissionTestCases",
       "options": [
         {
@@ -67,6 +75,9 @@
     },
     {
       "name": "CtsSafetyCenterTestCases"
+    },
+    {
+      "name": "SafetyCenterFunctionalTestCases"
     }
   ]
 }
diff --git a/PermissionController/src/com/android/permissioncontroller/role/Role.md b/PermissionController/src/com/android/permissioncontroller/role/Role.md
index fa9188a..bde9f86 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/Role.md
+++ b/PermissionController/src/com/android/permissioncontroller/role/Role.md
@@ -92,6 +92,9 @@
 - `visible`: Whether this role is visible to users. If a role is invisible (a.k.a. hidden) to users,
 users won't be able to find it in Settings, and apps won't be able to request it. The role can still
 be managed by system APIs and shell command.
+- `uiBehavior`: Optional name of a [`RoleUiBehavior`](ui/behavior/RoleUiBehavior.java) class to
+control certain role UI behavior in Java code, e.g. `DialerRoleUiBehavior`. This can be useful
+when the XML syntax cannot express certain UI behavior specific to the role.
 
 The following tags can be specified inside a `<role>` tag:
 
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/EncryptionUnawareConfirmationMixin.java b/PermissionController/src/com/android/permissioncontroller/role/model/EncryptionUnawareConfirmationMixin.java
index 3f1e7ed..567f1c7 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/EncryptionUnawareConfirmationMixin.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/model/EncryptionUnawareConfirmationMixin.java
@@ -25,6 +25,8 @@
 
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.role.utils.PackageUtils;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.RoleBehavior;
 
 /**
  * Mixin for {@link RoleBehavior#getConfirmationMessage(Role, String, Context)}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/VisibilityMixin.java b/PermissionController/src/com/android/permissioncontroller/role/model/VisibilityMixin.java
index 07afc60..90cda72 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/VisibilityMixin.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/model/VisibilityMixin.java
@@ -23,6 +23,9 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.RoleBehavior;
+
 /**
  * Mixin for {@link RoleBehavior#isVisibleAsUser(Role, UserHandle, Context)} that returns whether
  * the role should be visible from a corresponding boolean resource.
diff --git a/PermissionController/src/com/android/permissioncontroller/role/service/ClearUserDeniedReceiver.java b/PermissionController/src/com/android/permissioncontroller/role/service/ClearUserDeniedReceiver.java
index 862cbba..ae54154 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/service/ClearUserDeniedReceiver.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/service/ClearUserDeniedReceiver.java
@@ -22,7 +22,7 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.permissioncontroller.role.model.UserDeniedManager;
+import com.android.role.controller.model.UserDeniedManager;
 
 import java.util.Objects;
 
diff --git a/PermissionController/src/com/android/permissioncontroller/role/service/RoleControllerServiceImpl.java b/PermissionController/src/com/android/permissioncontroller/role/service/RoleControllerServiceImpl.java
index fbb9c2e..c1c7da4 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/service/RoleControllerServiceImpl.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/service/RoleControllerServiceImpl.java
@@ -29,9 +29,10 @@
 import androidx.annotation.WorkerThread;
 
 import com.android.permissioncontroller.permission.utils.CollectionUtils;
-import com.android.permissioncontroller.role.model.Role;
-import com.android.permissioncontroller.role.model.Roles;
 import com.android.permissioncontroller.role.utils.PackageUtils;
+import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.Roles;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -428,8 +429,8 @@
             return false;
         }
         ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, this);
-        if (applicationInfo == null || !role.isApplicationVisibleAsUser(applicationInfo,
-                Process.myUserHandle(), this)) {
+        if (applicationInfo == null || !RoleUiBehaviorUtils.isApplicationVisibleAsUser(role,
+                applicationInfo, Process.myUserHandle(), this)) {
             return false;
         }
         return true;
@@ -444,7 +445,8 @@
         if (!role.isAvailable(this)) {
             return false;
         }
-        return role.isVisibleAsUser(Process.myUserHandle(), this);
+
+        return RoleUiBehaviorUtils.isVisibleAsUser(role, Process.myUserHandle(), this);
     }
 
     private static boolean checkFlags(int flags, int allowedFlags) {
diff --git a/PermissionController/src/com/android/permissioncontroller/role/service/RoleSearchIndexablesProvider.java b/PermissionController/src/com/android/permissioncontroller/role/service/RoleSearchIndexablesProvider.java
index 7201acf..db909a4 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/service/RoleSearchIndexablesProvider.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/service/RoleSearchIndexablesProvider.java
@@ -27,8 +27,10 @@
 
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.permission.service.BaseSearchIndexablesProvider;
-import com.android.permissioncontroller.role.model.Role;
-import com.android.permissioncontroller.role.model.Roles;
+import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.RoleParserInitializer;
+import com.android.role.controller.model.Roles;
 
 /**
  * {@link android.provider.SearchIndexablesProvider} for roles.
@@ -41,6 +43,12 @@
     public static final String ACTION_MANAGE_SPECIAL_APP_ACCESS =
             "com.android.permissioncontroller.settingssearch.action.MANAGE_SPECIAL_APP_ACCESS";
 
+    @Override
+    public boolean onCreate() {
+        RoleParserInitializer.initialize();
+        return true;
+    }
+
     @Nullable
     @Override
     public Cursor queryRawData(@Nullable String[] projection) {
@@ -53,7 +61,8 @@
 
             long token = Binder.clearCallingIdentity();
             try {
-                if (!role.isAvailable(context) || !role.isVisible(context)) {
+                if (!role.isAvailable(context) || !RoleUiBehaviorUtils.isVisible(role,
+                        context)) {
                     continue;
                 }
             } finally {
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppActivity.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppActivity.java
index 0ddb6c3..52471cb 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppActivity.java
@@ -29,10 +29,11 @@
 
 import com.android.permissioncontroller.DeviceUtils;
 import com.android.permissioncontroller.R;
-import com.android.permissioncontroller.role.model.Role;
-import com.android.permissioncontroller.role.model.Roles;
 import com.android.permissioncontroller.role.ui.auto.AutoDefaultAppFragment;
 import com.android.permissioncontroller.role.ui.handheld.HandheldDefaultAppFragment;
+import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.Roles;
 
 /**
  * Activity for a default app.
@@ -86,7 +87,8 @@
             finish();
             return;
         }
-        if (!role.isVisibleAsUser(user, this)) {
+
+        if (!RoleUiBehaviorUtils.isVisibleAsUser(role, user, this)) {
             Log.e(LOG_TAG, "Role is invisible: " + roleName);
             finish();
             return;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java
index b4e4aaa..06e5ed2 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java
@@ -39,8 +39,9 @@
 
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.permission.utils.Utils;
-import com.android.permissioncontroller.role.model.Role;
-import com.android.permissioncontroller.role.model.Roles;
+import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.Roles;
 
 import java.util.List;
 import java.util.Objects;
@@ -208,7 +209,8 @@
 
         preference.setChecked(checked);
         if (applicationInfo != null) {
-            mRole.prepareApplicationPreferenceAsUser(preference, applicationInfo, mUser, context);
+            RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(mRole, preference,
+                    applicationInfo, mUser, context);
         }
 
         preferenceScreen.addPreference(preference);
@@ -238,8 +240,9 @@
             mViewModel.setNoneDefaultApp();
         } else {
             String packageName = key;
-            CharSequence confirmationMessage = mRole.getConfirmationMessage(packageName,
-                    requireContext());
+            CharSequence confirmationMessage =
+                    RoleUiBehaviorUtils.getConfirmationMessage(mRole, packageName,
+                            requireContext());
             if (confirmationMessage != null) {
                 DefaultAppConfirmationDialogFragment.show(packageName, confirmationMessage, this);
             } else {
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java
index 752dfb9..3c8af11 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java
@@ -40,8 +40,9 @@
 
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.permission.utils.Utils;
-import com.android.permissioncontroller.role.model.Role;
-import com.android.permissioncontroller.role.model.Roles;
+import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.Roles;
 
 import java.util.List;
 import java.util.Objects;
@@ -186,8 +187,8 @@
                 preference.setIcon(Utils.getBadgedIcon(context, holderApplicationInfo));
                 preference.setSummary(Utils.getAppLabel(holderApplicationInfo, context));
             }
-            role.preparePreferenceAsUser((TwoTargetPreference) preference, user, context);
-
+            RoleUiBehaviorUtils.preparePreferenceAsUser(role, (TwoTargetPreference) preference,
+                    user, context);
             preferenceGroup.addPreference(preference);
         }
     }
@@ -198,7 +199,7 @@
         Context context = requireContext();
         Role role = Roles.get(context).get(roleName);
         UserHandle user = preference.getExtras().getParcelable(Intent.EXTRA_USER);
-        Intent intent = role.getManageIntentAsUser(user, context);
+        Intent intent = RoleUiBehaviorUtils.getManageIntentAsUser(role, user, context);
         if (intent == null) {
             intent = DefaultAppActivity.createIntent(roleName, user, context);
         }
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java
index 0806520..c89e1f7 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java
@@ -30,7 +30,7 @@
 import androidx.lifecycle.ViewModel;
 import androidx.lifecycle.ViewModelProvider;
 
-import com.android.permissioncontroller.role.model.Role;
+import com.android.role.controller.model.Role;
 
 import java.util.List;
 
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java
index 0d52169..5167cfb 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java
@@ -34,10 +34,11 @@
 
 import com.android.permissioncontroller.PermissionControllerStatsLog;
 import com.android.permissioncontroller.permission.utils.CollectionUtils;
-import com.android.permissioncontroller.role.model.Role;
-import com.android.permissioncontroller.role.model.Roles;
-import com.android.permissioncontroller.role.model.UserDeniedManager;
 import com.android.permissioncontroller.role.utils.PackageUtils;
+import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.Roles;
+import com.android.role.controller.model.UserDeniedManager;
 
 import java.util.List;
 import java.util.Objects;
@@ -109,7 +110,7 @@
             return;
         }
 
-        if (!role.isVisible(this)) {
+        if (!RoleUiBehaviorUtils.isVisible(role, this)) {
             Log.e(LOG_TAG, "Role is invisible: " + mRoleName);
             reportRequestResult(
                     PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java
index 091a71c..686703a 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java
@@ -52,10 +52,10 @@
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.permission.utils.PackageRemovalMonitor;
 import com.android.permissioncontroller.permission.utils.Utils;
-import com.android.permissioncontroller.role.model.Role;
-import com.android.permissioncontroller.role.model.Roles;
-import com.android.permissioncontroller.role.model.UserDeniedManager;
 import com.android.permissioncontroller.role.utils.PackageUtils;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.Roles;
+import com.android.role.controller.model.UserDeniedManager;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleViewModel.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleViewModel.java
index d93e8f8..ae80f99 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleViewModel.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleViewModel.java
@@ -23,7 +23,7 @@
 import androidx.lifecycle.ViewModel;
 import androidx.lifecycle.ViewModelProvider;
 
-import com.android.permissioncontroller.role.model.Role;
+import com.android.role.controller.model.Role;
 
 /**
  * {@link ViewModel} for a role request.
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleItem.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleItem.java
index 8bca517..dab7090 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleItem.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleItem.java
@@ -20,7 +20,7 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.permissioncontroller.role.model.Role;
+import com.android.role.controller.model.Role;
 
 import java.util.List;
 
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListLiveData.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListLiveData.java
index 9742ed9..b9011bd 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListLiveData.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListLiveData.java
@@ -29,9 +29,10 @@
 import androidx.lifecycle.LiveData;
 
 import com.android.permissioncontroller.AsyncTaskLiveData;
-import com.android.permissioncontroller.role.model.Role;
-import com.android.permissioncontroller.role.model.Roles;
 import com.android.permissioncontroller.role.utils.PackageUtils;
+import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.Roles;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -96,7 +97,7 @@
                 continue;
             }
 
-            if (!role.isVisibleAsUser(mUser, mContext)) {
+            if (!RoleUiBehaviorUtils.isVisibleAsUser(role, mUser, mContext)) {
                 continue;
             }
 
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java
index 31042f9..3ccb1d8 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java
@@ -29,8 +29,9 @@
 import androidx.lifecycle.LiveData;
 
 import com.android.permissioncontroller.AsyncTaskLiveData;
-import com.android.permissioncontroller.role.model.Role;
 import com.android.permissioncontroller.role.utils.PackageUtils;
+import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
+import com.android.role.controller.model.Role;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -94,7 +95,8 @@
                         + qualifyingPackageName);
                 continue;
             }
-            if (!mRole.isApplicationVisibleAsUser(qualifyingApplicationInfo, mUser, mContext)) {
+            if (!RoleUiBehaviorUtils.isApplicationVisibleAsUser(mRole, qualifyingApplicationInfo,
+                    mUser, mContext)) {
                 continue;
             }
             boolean isHolderApplication = holderPackageNames.contains(qualifyingPackageName);
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java
index c86d48d..dbd4c7c 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoDefaultAppFragment.java
@@ -27,8 +27,8 @@
 
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
-import com.android.permissioncontroller.role.model.Role;
 import com.android.permissioncontroller.role.ui.DefaultAppChildFragment;
+import com.android.role.controller.model.Role;
 
 /** Screen to pick a default app for a particular {@link Role}. */
 public class AutoDefaultAppFragment extends AutoSettingsFrameFragment implements
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/AssistantRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/AssistantRoleUiBehavior.java
new file mode 100644
index 0000000..40bd7a3
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/AssistantRoleUiBehavior.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.role.ui.behavior;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.permissioncontroller.R;
+import com.android.permissioncontroller.role.model.VisibilityMixin;
+import com.android.role.controller.model.Role;
+
+/***
+ * Class for UI behavior of Assistant role
+ */
+public class AssistantRoleUiBehavior implements RoleUiBehavior {
+
+    @Override
+    public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
+            @NonNull Context context) {
+        return VisibilityMixin.isVisible("config_showDefaultAssistant", context);
+    }
+
+    @Nullable
+    @Override
+    public Intent getManageIntentAsUser(@NonNull Role role, @NonNull UserHandle user,
+            @NonNull Context context) {
+        boolean isAutomotive =
+                context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+
+        if (isAutomotive) {
+            return null;
+        }
+
+        return new Intent(Settings.ACTION_VOICE_INPUT_SETTINGS);
+    }
+
+    @Nullable
+    @Override
+    public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName,
+            @NonNull Context context) {
+        return context.getString(R.string.assistant_confirmation_message);
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/AutomotiveRoleBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/BrowserRoleUiBehavior.java
similarity index 60%
copy from PermissionController/src/com/android/permissioncontroller/role/model/AutomotiveRoleBehavior.java
copy to PermissionController/src/com/android/permissioncontroller/role/ui/behavior/BrowserRoleUiBehavior.java
index 3596c7d..018b0db 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/AutomotiveRoleBehavior.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/BrowserRoleUiBehavior.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -14,22 +14,24 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.permissioncontroller.role.ui.behavior;
 
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.os.UserHandle;
 
 import androidx.annotation.NonNull;
 
-/**
- * Class for behavior of the Automotive role.
+import com.android.permissioncontroller.R;
+import com.android.role.controller.model.Role;
+
+/***
+ * Class for UI behavior of Browser role
  */
-public class AutomotiveRoleBehavior implements RoleBehavior {
+public class BrowserRoleUiBehavior implements RoleUiBehavior {
 
     @Override
-    public boolean isAvailableAsUser(@NonNull Role role, @NonNull UserHandle user,
+    public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
             @NonNull Context context) {
-        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+        return context.getResources().getBoolean(R.bool.config_showBrowserRole);
     }
 }
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/DialerRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/DialerRoleUiBehavior.java
new file mode 100644
index 0000000..e6b8dab
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/DialerRoleUiBehavior.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.role.ui.behavior;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.UserHandle;
+import android.telecom.TelecomManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+
+import com.android.permissioncontroller.R;
+import com.android.permissioncontroller.role.model.EncryptionUnawareConfirmationMixin;
+import com.android.role.controller.model.Role;
+
+import java.util.Objects;
+
+/***
+ * Class for UI behavior of Dialer role
+ */
+public class DialerRoleUiBehavior implements RoleUiBehavior {
+
+    @Override
+    public void prepareApplicationPreferenceAsUser(@NonNull Role role,
+            @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo,
+            @NonNull UserHandle user, @NonNull Context context) {
+        TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
+        String systemPackageName = telecomManager.getSystemDialerPackage();
+        if (Objects.equals(applicationInfo.packageName, systemPackageName)) {
+            preference.setSummary(R.string.default_app_system_default);
+        } else {
+            preference.setSummary(null);
+        }
+    }
+
+    @Override
+    public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
+            @NonNull Context context) {
+        return context.getResources().getBoolean(R.bool.config_showDialerRole);
+    }
+
+    @Nullable
+    @Override
+    public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName,
+            @NonNull Context context) {
+        return EncryptionUnawareConfirmationMixin.getConfirmationMessage(role, packageName,
+                context);
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/EmergencyRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/EmergencyRoleUiBehavior.java
new file mode 100644
index 0000000..8a62ee7
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/EmergencyRoleUiBehavior.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.role.ui.behavior;
+
+import android.content.Context;
+import android.os.UserHandle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.permissioncontroller.role.model.EncryptionUnawareConfirmationMixin;
+import com.android.permissioncontroller.role.model.VisibilityMixin;
+import com.android.role.controller.model.Role;
+
+/***
+ * Class for UI behavior of Emergency role
+ */
+public class EmergencyRoleUiBehavior implements RoleUiBehavior {
+
+    @Override
+    public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
+            @NonNull Context context) {
+        return VisibilityMixin.isVisible("config_showDefaultEmergency", context);
+    }
+
+    @Nullable
+    @Override
+    public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName,
+            @NonNull Context context) {
+        return EncryptionUnawareConfirmationMixin.getConfirmationMessage(role, packageName,
+                context);
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/HomeRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/HomeRoleUiBehavior.java
new file mode 100644
index 0000000..ac8538b
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/HomeRoleUiBehavior.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.role.ui.behavior;
+
+import android.app.admin.DevicePolicyResources;
+import android.app.role.RoleManager;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Build;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.preference.Preference;
+
+import com.android.permissioncontroller.R;
+import com.android.permissioncontroller.permission.utils.CollectionUtils;
+import com.android.permissioncontroller.permission.utils.Utils;
+import com.android.permissioncontroller.role.model.VisibilityMixin;
+import com.android.permissioncontroller.role.ui.TwoTargetPreference;
+import com.android.permissioncontroller.role.utils.UserUtils;
+import com.android.role.controller.model.HomeRoleBehavior;
+import com.android.role.controller.model.Role;
+
+/***
+ * Class for UI behavior of Home role
+ */
+public class HomeRoleUiBehavior implements RoleUiBehavior {
+
+    private static final String LOG_TAG = HomeRoleUiBehavior.class.getSimpleName();
+
+    @Override
+    public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
+            @NonNull Context context) {
+        return VisibilityMixin.isVisible("config_showDefaultHome", context);
+    }
+
+    @Override
+    public void preparePreferenceAsUser(@NonNull Role role, @NonNull TwoTargetPreference preference,
+            @NonNull UserHandle user, @NonNull Context context) {
+        TwoTargetPreference.OnSecondTargetClickListener listener = null;
+        RoleManager roleManager = context.getSystemService(RoleManager.class);
+        String packageName = CollectionUtils.firstOrNull(roleManager.getRoleHoldersAsUser(
+                role.getName(), user));
+        if (packageName != null) {
+            Intent intent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
+                    .setPackage(packageName)
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+            PackageManager userPackageManager = UserUtils.getUserContext(context, user)
+                    .getPackageManager();
+            ResolveInfo resolveInfo = userPackageManager.resolveActivity(intent, 0);
+            if (resolveInfo != null && resolveInfo.activityInfo != null
+                    && resolveInfo.activityInfo.exported) {
+                listener = preference2 -> {
+                    try {
+                        context.startActivity(intent);
+                    } catch (ActivityNotFoundException e) {
+                        Log.e(LOG_TAG, "Cannot start activity for home app preferences", e);
+                    }
+                };
+            }
+        }
+        preference.setOnSecondTargetClickListener(listener);
+    }
+
+    @Override
+    public boolean isApplicationVisibleAsUser(@NonNull Role role,
+            @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user,
+            @NonNull Context context) {
+        // Home is not available for work profile, so we can just use the current user.
+        return !HomeRoleBehavior.isSettingsApplication(applicationInfo, context);
+    }
+
+    @Override
+    public void prepareApplicationPreferenceAsUser(@NonNull Role role,
+            @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo,
+            @NonNull UserHandle user, @NonNull Context context) {
+        boolean missingWorkProfileSupport = isMissingWorkProfileSupport(applicationInfo, context);
+        preference.setEnabled(!missingWorkProfileSupport);
+        preference.setSummary(missingWorkProfileSupport ? Utils.getEnterpriseString(context,
+                DevicePolicyResources.Strings.DefaultAppSettings
+                        .HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE,
+                R.string.home_missing_work_profile_support) : null);
+    }
+
+    private boolean isMissingWorkProfileSupport(@NonNull ApplicationInfo applicationInfo,
+            @NonNull Context context) {
+        boolean hasWorkProfile = UserUtils.getWorkProfile(context) != null;
+        if (!hasWorkProfile) {
+            return false;
+        }
+        boolean isWorkProfileSupported = applicationInfo.targetSdkVersion
+                >= Build.VERSION_CODES.LOLLIPOP;
+        return !isWorkProfileSupported;
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/RoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/RoleUiBehavior.java
new file mode 100644
index 0000000..a719040
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/RoleUiBehavior.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.role.ui.behavior;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.os.UserHandle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+
+import com.android.permissioncontroller.role.ui.TwoTargetPreference;
+import com.android.role.controller.model.Role;
+
+/***
+ * Interface for UI behavior for roles
+ */
+public interface RoleUiBehavior {
+
+    /**
+     * Check whether this role should be visible to user.
+     *
+     * @param role the role to check for
+     * @param user the user to check for
+     * @param context the `Context` to retrieve system services
+     *
+     * @return whether this role should be visible to user
+     */
+    default boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
+            @NonNull Context context) {
+        return true;
+    }
+
+    /**
+     * Get the {@link Intent} to manage this role, or {@code null} to use the default UI.
+     *
+     * @param role the role to get the intent for
+     * @param user the user to manage this role for
+     * @param context the {@code Context} to retrieve system services
+     *
+     * @return the {@link Intent} to manage this role, or {@code null} to use the default UI.
+     */
+    @Nullable
+    default Intent getManageIntentAsUser(@NonNull Role role, @NonNull UserHandle user,
+            @NonNull Context context) {
+        return null;
+    }
+
+    /**
+     * Prepare a {@link Preference} for this role.
+     *
+     * @param role the role to prepare the preference for
+     * @param preference the {@link Preference} for this role
+     * @param user the user for this role
+     * @param context the {@code Context} to retrieve system services
+     */
+    default void preparePreferenceAsUser(@NonNull Role role,
+            @NonNull TwoTargetPreference preference,
+            @NonNull UserHandle user,
+            @NonNull Context context) {
+    }
+
+    /**
+     * Check whether a qualifying application should be visible to user.
+     *
+     * @param applicationInfo the {@link ApplicationInfo} for the application
+     * @param user the user for the application
+     * @param context the {@code Context} to retrieve system services
+     *
+     * @return whether the qualifying application should be visible to user
+     */
+    default boolean isApplicationVisibleAsUser(@NonNull Role role,
+            @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user,
+            @NonNull Context context) {
+        return true;
+    }
+
+    /**
+     * Prepare a {@link Preference} for this role.
+     *
+     * @param role the role to prepare the preference for
+     * @param preference the {@link Preference} for this role
+     * @param user the user for this role
+     * @param context the {@code Context} to retrieve system services
+     */
+    default void prepareApplicationPreferenceAsUser(@NonNull Role role,
+            @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo,
+            @NonNull UserHandle user, @NonNull Context context) {
+    }
+
+    /**
+     * Get the confirmation message for adding an application as a holder of this role.
+     *
+     * @param role the role to get confirmation message for
+     * @param packageName the package name of the application to get confirmation message for
+     * @param context the {@code Context} to retrieve system services
+     *
+     * @return the confirmation message, or {@code null} if no confirmation is needed
+     */
+    @Nullable
+    default CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName,
+            @NonNull Context context) {
+        return null;
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/SmsRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/SmsRoleUiBehavior.java
new file mode 100644
index 0000000..9fc9be3
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/SmsRoleUiBehavior.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.role.ui.behavior;
+
+import android.content.Context;
+import android.os.UserHandle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.permissioncontroller.R;
+import com.android.permissioncontroller.role.model.EncryptionUnawareConfirmationMixin;
+import com.android.role.controller.model.Role;
+
+/***
+ * Class for UI behavior of SMS role
+ */
+public class SmsRoleUiBehavior implements RoleUiBehavior {
+
+    @Override
+    public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
+            @NonNull Context context) {
+        return context.getResources().getBoolean(R.bool.config_showSmsRole);
+    }
+
+    @Nullable
+    @Override
+    public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName,
+            @NonNull Context context) {
+        return EncryptionUnawareConfirmationMixin.getConfirmationMessage(role, packageName,
+                context);
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessActivity.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessActivity.java
index 2328bb9..6ed1051 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessActivity.java
@@ -27,11 +27,12 @@
 
 import com.android.permissioncontroller.DeviceUtils;
 import com.android.permissioncontroller.R;
-import com.android.permissioncontroller.role.model.Role;
-import com.android.permissioncontroller.role.model.Roles;
 import com.android.permissioncontroller.role.ui.SettingsActivity;
 import com.android.permissioncontroller.role.ui.auto.AutoSpecialAppAccessFragment;
 import com.android.permissioncontroller.role.ui.specialappaccess.handheld.HandheldSpecialAppAccessFragment;
+import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.Roles;
 
 /**
  * Activity for a special app access.
@@ -75,7 +76,8 @@
             finish();
             return;
         }
-        if (!role.isVisible(this)) {
+
+        if (!RoleUiBehaviorUtils.isVisible(role, this)) {
             Log.e(LOG_TAG, "Role is invisible: " + roleName);
             finish();
             return;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java
index 66e1e53..d75747b 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java
@@ -36,9 +36,10 @@
 import androidx.preference.TwoStatePreference;
 
 import com.android.permissioncontroller.permission.utils.Utils;
-import com.android.permissioncontroller.role.model.Role;
-import com.android.permissioncontroller.role.model.Roles;
 import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData;
+import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.Roles;
 
 import java.util.List;
 
@@ -157,9 +158,8 @@
 
             preference.setChecked(isHolderPackage);
             UserHandle user = UserHandle.getUserHandleForUid(qualifyingApplicationInfo.uid);
-            mRole.prepareApplicationPreferenceAsUser(preference, qualifyingApplicationInfo, user,
-                    context);
-
+            RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(mRole, preference,
+                    qualifyingApplicationInfo, user, context);
             preferenceScreen.addPreference(preference);
         }
 
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java
index 52b7aa0..ec4de84 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java
@@ -32,10 +32,11 @@
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
 
-import com.android.permissioncontroller.role.model.Role;
-import com.android.permissioncontroller.role.model.Roles;
 import com.android.permissioncontroller.role.ui.RoleItem;
 import com.android.permissioncontroller.role.ui.TwoTargetPreference;
+import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.Roles;
 
 import java.util.List;
 
@@ -110,10 +111,9 @@
                 preference.setPersistent(false);
                 preference.setOnPreferenceClickListener(this);
             }
-
-            role.preparePreferenceAsUser((TwoTargetPreference) preference, Process.myUserHandle(),
+            RoleUiBehaviorUtils.preparePreferenceAsUser(role, (TwoTargetPreference) preference,
+                    Process.myUserHandle(),
                     context);
-
             preferenceScreen.addPreference(preference);
         }
 
@@ -126,7 +126,7 @@
         Context context = requireContext();
         Role role = Roles.get(context).get(roleName);
         UserHandle user = Process.myUserHandle();
-        Intent intent = role.getManageIntentAsUser(user, context);
+        Intent intent = RoleUiBehaviorUtils.getManageIntentAsUser(role, user, context);
         if (intent == null) {
             intent = SpecialAppAccessActivity.createIntent(roleName, context);
         }
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java
index 03aa540..0cc00ab 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java
@@ -32,11 +32,11 @@
 import androidx.lifecycle.ViewModel;
 import androidx.lifecycle.ViewModelProvider;
 
-import com.android.permissioncontroller.role.model.Role;
 import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData;
 import com.android.permissioncontroller.role.ui.RoleLiveData;
 import com.android.permissioncontroller.role.ui.RoleSortFunction;
 import com.android.permissioncontroller.role.utils.UserUtils;
+import com.android.role.controller.model.Role;
 
 import java.util.List;
 
diff --git a/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java b/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java
new file mode 100644
index 0000000..e60bc6d
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.role.utils;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+
+import com.android.permissioncontroller.role.ui.TwoTargetPreference;
+import com.android.permissioncontroller.role.ui.behavior.RoleUiBehavior;
+import com.android.role.controller.model.Role;
+
+/**
+ * Utility methods for Role UI behavior
+ */
+public final class RoleUiBehaviorUtils {
+
+    private static final String LOG_TAG = RoleUiBehaviorUtils.class.getSimpleName();
+
+    /**
+     * Get the role ui behavior if available
+     */
+    @Nullable
+    private static RoleUiBehavior getUiBehavior(@NonNull Role role) {
+        String uiBehaviorName = role.getUiBehaviorName();
+        if (uiBehaviorName == null) {
+            return null;
+        }
+        RoleUiBehavior uiBehavior;
+        String uiBehaviorClassName = RoleUiBehavior.class.getPackage().getName() + '.'
+                + uiBehaviorName;
+        try {
+            uiBehavior = (RoleUiBehavior) Class.forName(uiBehaviorClassName).newInstance();
+        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
+            Log.e(LOG_TAG, "Unable to instantiate UI behavior: " + uiBehaviorClassName, e);
+            return null;
+        }
+        return uiBehavior;
+    }
+
+    /**
+     * @see RoleUiBehavior#isVisibleAsUser
+     */
+    public static boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
+            @NonNull Context context) {
+        RoleUiBehavior uiBehavior = getUiBehavior(role);
+        if (uiBehavior == null) {
+            return role.isVisible();
+        }
+        return role.isVisible() && uiBehavior.isVisibleAsUser(role, user, context);
+    }
+
+    /**
+     * Check whether this role should be visible to user, for current user.
+     *
+     * @param context the `Context` to retrieve system services
+     *
+     * @return whether this role should be visible to user.
+     */
+    public static boolean isVisible(@NonNull Role role, @NonNull Context context) {
+        return isVisibleAsUser(role, Process.myUserHandle(), context);
+    }
+
+
+    /**
+     * @see RoleUiBehavior#getManageIntentAsUser
+     */
+    @Nullable
+    public static Intent getManageIntentAsUser(@NonNull Role role, @NonNull UserHandle user,
+            @NonNull Context context) {
+        RoleUiBehavior uiBehavior = getUiBehavior(role);
+        if (uiBehavior == null) {
+            return null;
+        }
+        return uiBehavior.getManageIntentAsUser(role, user, context);
+    }
+
+    /**
+     * @see RoleUiBehavior#preparePreferenceAsUser
+     */
+    public static void preparePreferenceAsUser(@NonNull Role role,
+            @NonNull TwoTargetPreference preference, @NonNull UserHandle user,
+            @NonNull Context context) {
+        RoleUiBehavior uiBehavior = getUiBehavior(role);
+        if (uiBehavior == null) {
+            return;
+        }
+        uiBehavior.preparePreferenceAsUser(role, preference, user, context);
+    }
+
+    /**
+     * @see RoleUiBehavior#isApplicationVisibleAsUser
+     */
+    public static boolean isApplicationVisibleAsUser(@NonNull Role role,
+            @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user,
+            @NonNull Context context) {
+        RoleUiBehavior uiBehavior = getUiBehavior(role);
+        if (uiBehavior == null) {
+            return true;
+        }
+        return uiBehavior.isApplicationVisibleAsUser(role, applicationInfo, user, context);
+    }
+
+    /**
+     * @see RoleUiBehavior#prepareApplicationPreferenceAsUser
+     */
+    public static void prepareApplicationPreferenceAsUser(@NonNull Role role,
+            @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo,
+            @NonNull UserHandle user, @NonNull Context context) {
+        RoleUiBehavior uiBehavior = getUiBehavior(role);
+        if (uiBehavior == null) {
+            return;
+        }
+        uiBehavior.prepareApplicationPreferenceAsUser(role, preference, applicationInfo, user,
+                context);
+    }
+
+    /**
+     * @see RoleUiBehavior#getConfirmationMessage
+     */
+    @Nullable
+    public static CharSequence getConfirmationMessage(@NonNull Role role,
+            @NonNull String packageName, @NonNull Context context) {
+        RoleUiBehavior uiBehavior = getUiBehavior(role);
+        if (uiBehavior == null) {
+            return null;
+        }
+        return uiBehavior.getConfirmationMessage(role, packageName, context);
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java b/PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java
index ecaee66..e89470f 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java
@@ -39,16 +39,41 @@
     /**
      * Check whether a user is a profile.
      *
-     * @param user the user to check
+     * @param user    the user to check
      * @param context the {@code Context} to retrieve system services
-     *
      * @return whether the user is a profile
      */
     public static boolean isProfile(@NonNull UserHandle user, @NonNull Context context) {
+        return isManagedProfile(user, context) || isCloneProfile(user, context);
+    }
+
+    /**
+     * Check whether a user is a managed profile.
+     *
+     * @param user    the user to check
+     * @param context the {@code Context} to retrieve system services
+     * @return whether the user is a managed profile
+     */
+    public static boolean isManagedProfile(@NonNull UserHandle user, @NonNull Context context) {
         Context userContext = getUserContext(context, user);
         UserManager userUserManager = userContext.getSystemService(UserManager.class);
-        return userUserManager.isManagedProfile(user.getIdentifier()) || (
-                Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && userUserManager.isCloneProfile());
+        return userUserManager.isManagedProfile(user.getIdentifier());
+    }
+
+    /**
+     * Check whether a user is a clone profile.
+     *
+     * @param user    the user to check
+     * @param context the {@code Context} to retrieve system services
+     * @return whether the user is a clone profile
+     */
+    public static boolean isCloneProfile(@NonNull UserHandle user, @NonNull Context context) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
+            return false;
+        }
+        Context userContext = getUserContext(context, user);
+        UserManager userUserManager = userContext.getSystemService(UserManager.class);
+        return userUserManager.isCloneProfile();
     }
 
     /**
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/safetycenter/TEST_MAPPING
index 5feb2e9..c702ee8 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/TEST_MAPPING
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/TEST_MAPPING
@@ -7,11 +7,22 @@
           "exclude-annotation": "com.android.bedstead.harrier.annotations.Postsubmit"
         }
       ]
+    },
+    {
+      "name": "SafetyCenterFunctionalTestCases",
+      "options": [
+        {
+          "exclude-annotation": "com.android.bedstead.harrier.annotations.Postsubmit"
+        }
+      ]
     }
   ],
   "postsubmit": [
     {
       "name": "CtsSafetyCenterTestCases"
+    },
+    {
+      "name": "SafetyCenterFunctionalTestCases"
     }
   ],
   "mainline-presubmit": [
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java
index eb4a84a..df1aed7 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java
@@ -19,12 +19,11 @@
 import static android.app.job.JobScheduler.RESULT_SUCCESS;
 import static android.content.Intent.ACTION_BOOT_COMPLETED;
 import static android.safetycenter.SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED;
-import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_DEVICE_REBOOT;
+import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_OTHER;
+import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_PERIODIC;
 
 import static com.android.permissioncontroller.Constants.SAFETY_CENTER_BACKGROUND_REFRESH_JOB_ID;
 
-import static java.util.Objects.requireNonNull;
-
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
 import android.app.job.JobScheduler;
@@ -39,62 +38,122 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.modules.utils.build.SdkLevel;
+
 /**
- * Uses {@link android.app.job.JobScheduler} to schedule one-off calls to {@link
- * SafetyCenterManager#refreshSafetySources} after boot completed and after safety center is
- * enabled.
+ * Uses {@link android.app.job.JobScheduler} to schedule periodic calls to {@link
+ * SafetyCenterManager#refreshSafetySources} after boot completed if safety center is already
+ * enabled, or after safety center is enabled otherwise.
  *
- * <p>The job waits until the device is in idle maintenance mode to minimize impact on system
- * health.
+ * <p>The job waits until the device is in idle mode to minimize impact on system health.
  */
 // TODO(b/243493200): Add tests
-// TODO(b/243537828): Consider disabling this during other tests in case it makes them flakey
 public final class SafetyCenterBackgroundRefreshJobService extends JobService {
     private static final String TAG = "SafetyCenterBackgroundR";
 
+    /** Schedules a periodic background refresh. */
+    public static final class SetupSafetyCenterBackgroundRefreshReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(@NonNull Context context, @NonNull Intent intent) {
+            schedulePeriodicBackgroundRefresh(context, intent.getAction());
+        }
+    }
+
     /**
-     * Schedules a one-off call to {@link SafetyCenterManager#refreshSafetySources} to be run when
-     * the device is idle.
+     * Schedules a periodic call to {@link SafetyCenterManager#refreshSafetySources} to be run when
+     * the device is idle, after either {@link android.content.Intent#ACTION_BOOT_COMPLETED} or
+     * {@link android.safetycenter.SafetyCenterManager#ACTION_SAFETY_CENTER_ENABLED_CHANGED}.
+     *
+     * <p>The {@link SafetyCenterManager#isSafetyCenterEnabled} check ensures that jobs are never
+     * scheduled if SafetyCenter is disabled, we check again in {@link
+     * SafetyCenterBackgroundRefreshJobService#onStartJob} in case SafetyCenter becomes disabled
+     * later.
+     *
+     * <p>{@link SafetyCenterJobServiceFlags#areBackgroundRefreshesEnabled} is only checked in
+     * {@link SafetyCenterBackgroundRefreshJobService#onStartJob} as we do not receive a new
+     * broadcast if this flag gets enabled.
      */
-    public static void scheduleOneOffBackgroundRefresh(
+    private static void schedulePeriodicBackgroundRefresh(
             @NonNull Context context, @Nullable String actionString) {
 
-        if (!(ACTION_BOOT_COMPLETED.equals(actionString)
-                || ACTION_SAFETY_CENTER_ENABLED_CHANGED.equals(actionString))) {
+        if (!isActionStringValid(actionString)) {
+            Log.v(TAG, "Ignoring a " + actionString + " broadcast.");
             return;
         }
 
         SafetyCenterManager safetyCenterManager =
-                requireNonNull(context.getSystemService(SafetyCenterManager.class));
-        if (!safetyCenterManager.isSafetyCenterEnabled()) {
+                context.getSystemService(SafetyCenterManager.class);
+        if (safetyCenterManager == null) {
+            Log.w(TAG, "SafetyCenterManager is null, cannot schedule background refresh.");
             return;
         }
 
-        Log.v(TAG, "Scheduling a one-off background refresh.");
-        JobScheduler jobScheduler = requireNonNull(context.getSystemService(JobScheduler.class));
-        JobInfo.Builder builder =
-                (new JobInfo.Builder(
+        JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+        if (jobScheduler == null) {
+            Log.w(TAG, "JobScheduler is null, cannot schedule background refresh.");
+            return;
+        }
+
+        if (!safetyCenterManager.isSafetyCenterEnabled()) {
+            Log.v(
+                    TAG,
+                    "Received a "
+                            + actionString
+                            + " broadcast, but safety center is currently disabled. Cancelling any"
+                            + " existing job.");
+            jobScheduler.cancel(SAFETY_CENTER_BACKGROUND_REFRESH_JOB_ID);
+            return;
+        }
+
+        JobInfo jobInfo =
+                new JobInfo.Builder(
                                 SAFETY_CENTER_BACKGROUND_REFRESH_JOB_ID,
                                 new ComponentName(
-                                        context, SafetyCenterBackgroundRefreshJobService.class)))
-                        .setRequiresDeviceIdle(true);
-        int scheduleResult = jobScheduler.schedule(builder.build());
+                                        context, SafetyCenterBackgroundRefreshJobService.class))
+                        .setRequiresDeviceIdle(true)
+                        .setRequiresCharging(
+                                SafetyCenterJobServiceFlags.getBackgroundRefreshRequiresCharging())
+                        .setPeriodic(
+                                SafetyCenterJobServiceFlags.getPeriodicBackgroundRefreshInterval()
+                                        .toMillis())
+                        .build();
+
+        Log.v(
+                TAG,
+                "Scheduling a periodic background refresh with "
+                        + ", interval="
+                        + jobInfo.getIntervalMillis()
+                        + "requires charging="
+                        + jobInfo.isRequireCharging());
+
+        int scheduleResult = jobScheduler.schedule(jobInfo);
         if (scheduleResult != RESULT_SUCCESS) {
-            Log.e(TAG, "Could not schedule background refresh, scheduleResult=" + scheduleResult);
+            Log.e(
+                    TAG,
+                    "Could not schedule the background refresh job, scheduleResult="
+                            + scheduleResult);
         }
     }
 
     @Override
     public boolean onStartJob(@NonNull JobParameters params) {
         // background thread not required, PC APK makes all API calls in main thread
-        Log.v(TAG, "Background refresh job has started.");
-        SafetyCenterManager safetyCenterManager =
-                requireNonNull(this.getSystemService(SafetyCenterManager.class));
-        if (safetyCenterManager.isSafetyCenterEnabled()) {
-            // TODO(b/243523521): Use the correct refresh reason depending on which intent the
-            // receiver receives
-            safetyCenterManager.refreshSafetySources(REFRESH_REASON_DEVICE_REBOOT);
+        if (!SafetyCenterJobServiceFlags.areBackgroundRefreshesEnabled()) {
+            Log.v(TAG, "Background refreshes are not enabled, skipping job.");
+            return false; // job is no longer running
         }
+        SafetyCenterManager safetyCenterManager = this.getSystemService(SafetyCenterManager.class);
+        if (safetyCenterManager == null) {
+            Log.w(TAG, "Safety center manager is null, skipping job.");
+            return false; // job is no longer running
+        }
+        if (!safetyCenterManager.isSafetyCenterEnabled()) {
+            Log.v(TAG, "Safety center is not enabled, skipping job.");
+            return false; // job is no longer running
+        }
+
+        Log.v(TAG, "Background refresh job has started.");
+        safetyCenterManager.refreshSafetySources(getRefreshReason());
         return false; // job is no longer running
     }
 
@@ -103,13 +162,15 @@
         return false; // never want job to be rescheduled
     }
 
-    /**
-     * Schedules a background refresh on boot completed and when safety center is enabled.
-     */
-    public static final class SetupSafetyCenterBackgroundRefreshReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(@NonNull Context context, @NonNull Intent intent) {
-            scheduleOneOffBackgroundRefresh(context, intent.getAction());
+    private static boolean isActionStringValid(@Nullable String actionString) {
+        return ACTION_BOOT_COMPLETED.equals(actionString)
+                || ACTION_SAFETY_CENTER_ENABLED_CHANGED.equals(actionString);
+    }
+
+    private static int getRefreshReason() {
+        if (SdkLevel.isAtLeastU()) {
+            return REFRESH_REASON_PERIODIC;
         }
+        return REFRESH_REASON_OTHER;
     }
 }
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterJobServiceFlags.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterJobServiceFlags.java
new file mode 100644
index 0000000..bdca4d7
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterJobServiceFlags.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.safetycenter.service;
+
+import android.app.job.JobService;
+import android.provider.DeviceConfig;
+
+import java.time.Duration;
+
+/** A class so that the Safety Center {@link JobService} can access {@link DeviceConfig} flags. */
+public class SafetyCenterJobServiceFlags {
+    private static final Duration DEFAULT_PERIODIC_BACKGROUND_REFRESH_INTERVAL = Duration.ofDays(1);
+    private static final String PROPERTY_BACKGROUND_REFRESH_IS_ENABLED =
+            "safety_center_background_refresh_is_enabled";
+    private static final String PROPERTY_BACKGROUND_REFRESH_REQUIRES_CHARGING =
+            "safety_center_background_requires_charging";
+    private static final String PROPERTY_PERIODIC_BACKGROUND_REFRESH_INTERVAL_MILLIS =
+            "safety_center_periodic_background_interval_millis";
+
+    /** Returns whether background refreshes should be enabled. */
+    static boolean areBackgroundRefreshesEnabled() {
+        return DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_BACKGROUND_REFRESH_IS_ENABLED, true);
+    }
+
+    /**
+     * Returns the interval that should be used when scheduling periodic background refresh jobs.
+     */
+    static Duration getPeriodicBackgroundRefreshInterval() {
+        return Duration.ofMillis(
+                DeviceConfig.getLong(
+                        DeviceConfig.NAMESPACE_PRIVACY,
+                        PROPERTY_PERIODIC_BACKGROUND_REFRESH_INTERVAL_MILLIS,
+                        DEFAULT_PERIODIC_BACKGROUND_REFRESH_INTERVAL.toMillis()));
+    }
+
+    /**
+     * Returns whether we should constrain background refresh jobs to only run when the device is
+     * charging.
+     */
+    static boolean getBackgroundRefreshRequiresCharging() {
+        return DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_PRIVACY,
+                PROPERTY_BACKGROUND_REFRESH_REQUIRES_CHARGING,
+                true);
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/InteractionLogger.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/InteractionLogger.kt
index a9f051b..02d617b 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/InteractionLogger.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/InteractionLogger.kt
@@ -44,8 +44,35 @@
     var navigationSource: NavigationSource = NavigationSource.UNKNOWN,
     var navigationSensor: Sensor = Sensor.UNKNOWN
 ) {
-    @JvmOverloads
-    fun record(
+    fun record(action: Action) {
+        writeAtom(action)
+    }
+
+    fun recordForIssue(action: Action, issue: SafetyCenterIssue) {
+        val decodedId = SafetyCenterIds.issueIdFromString(issue.id)
+        writeAtom(
+            action,
+            LogSeverityLevel.fromIssueSeverityLevel(issue.severityLevel),
+            sourceId = decodedId.safetyCenterIssueKey.safetySourceId,
+            sourceProfileType =
+                SafetySourceProfileType.fromUserId(decodedId.safetyCenterIssueKey.userId),
+            issueTypeId = decodedId.issueTypeId)
+    }
+
+    fun recordForEntry(action: Action, entry: SafetyCenterEntry) {
+        val decodedId = SafetyCenterIds.entryIdFromString(entry.id)
+        writeAtom(
+            action,
+            LogSeverityLevel.fromEntrySeverityLevel(entry.severityLevel),
+            sourceId = decodedId.safetySourceId,
+            sourceProfileType = SafetySourceProfileType.fromUserId(decodedId.userId))
+    }
+
+    fun recordForSensor(action: Action, sensor: Sensor) {
+        writeAtom(action = action, sensor = sensor)
+    }
+
+    private fun writeAtom(
         action: Action,
         severityLevel: LogSeverityLevel = LogSeverityLevel.UNKNOWN,
         sourceId: String? = null,
@@ -57,6 +84,11 @@
             return
         }
 
+        // WARNING: Be careful when logging severity levels. If the severity level being recorded
+        // is at all influenced by a logging-disallowed source, we should not record it. At the
+        // moment, we do not record overall severity levels in this atom, but leaving this note for
+        // future implementors.
+
         PermissionControllerStatsLog.write(
             PermissionControllerStatsLog.SAFETY_CENTER_INTERACTION_REPORTED,
             sessionId,
@@ -70,30 +102,6 @@
             (if (sensor != Sensor.UNKNOWN) sensor else navigationSensor).statsLogValue)
     }
 
-    fun recordForIssue(action: Action, issue: SafetyCenterIssue) {
-        val decodedId = SafetyCenterIds.issueIdFromString(issue.id)
-        record(
-            action,
-            LogSeverityLevel.fromIssueSeverityLevel(issue.severityLevel),
-            sourceId = decodedId.safetyCenterIssueKey.safetySourceId,
-            sourceProfileType =
-                SafetySourceProfileType.fromUserId(decodedId.safetyCenterIssueKey.userId),
-            issueTypeId = decodedId.issueTypeId)
-    }
-
-    fun recordForEntry(action: Action, entry: SafetyCenterEntry) {
-        val decodedId = SafetyCenterIds.entryIdFromString(entry.id)
-        record(
-            action,
-            LogSeverityLevel.fromEntrySeverityLevel(entry.severityLevel),
-            sourceId = decodedId.safetySourceId,
-            sourceProfileType = SafetySourceProfileType.fromUserId(decodedId.userId))
-    }
-
-    fun recordForSensor(action: Action, sensor: Sensor) {
-        record(action = action, sensor = sensor)
-    }
-
     private companion object {
         /**
          * Encodes a string into an long ID. The ID is a SHA-256 of the string, truncated to 64
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java
index ce32108..4c6bee7 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/IssueCardPreference.java
@@ -46,6 +46,7 @@
 import androidx.preference.Preference;
 import androidx.preference.PreferenceViewHolder;
 
+import com.android.modules.utils.build.SdkLevel;
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel;
 import com.android.safetycenter.internaldata.SafetyCenterIds;
@@ -102,9 +103,19 @@
         ((TextView) holder.findViewById(R.id.issue_card_title)).setText(mIssue.getTitle());
         ((TextView) holder.findViewById(R.id.issue_card_summary)).setText(mIssue.getSummary());
 
+        CharSequence attributionTitle = SdkLevel.isAtLeastU() ? mIssue.getAttributionTitle() : null;
+        TextView attributionTitleTextView =
+                (TextView) holder.findViewById(R.id.issue_card_attribution_title);
+        if (TextUtils.isEmpty(attributionTitle)) {
+            attributionTitleTextView.setVisibility(View.GONE);
+        } else {
+            attributionTitleTextView.setText(attributionTitle);
+            attributionTitleTextView.setVisibility(View.VISIBLE);
+        }
         CharSequence subtitle = mIssue.getSubtitle();
         TextView subtitleTextView = (TextView) holder.findViewById(R.id.issue_card_subtitle);
         CharSequence contentDescription;
+        // TODO(b/257972736): Add a11y support for attribution title.
         if (TextUtils.isEmpty(subtitle)) {
             subtitleTextView.setVisibility(View.GONE);
             contentDescription =
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt
index 6e1f99d..ec82ac7 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardPreference.kt
@@ -28,6 +28,7 @@
 import androidx.preference.PreferenceViewHolder
 import com.android.permissioncontroller.R
 import com.android.permissioncontroller.permission.utils.StringUtils
+import java.text.NumberFormat
 
 /** A preference that displays a card linking to a list of more {@link SafetyCenterIssue}. */
 class MoreIssuesCardPreference(
@@ -69,8 +70,9 @@
     private fun updateHiddenIssueCount(textView: TextView) {
         moreIssuesCardAnimator.cancelTextChangeAnimation(textView)
 
-        val previousText = previousMoreIssuesCardData?.hiddenIssueCount.toString()
-        val newText = newMoreIssuesCardData.hiddenIssueCount.toString()
+        val numberFormat = NumberFormat.getInstance()
+        val previousText = previousMoreIssuesCardData?.hiddenIssueCount?.let(numberFormat::format)
+        val newText = numberFormat.format(newMoreIssuesCardData.hiddenIssueCount)
         val animateTextChange = !previousText.isNullOrEmpty() && previousText != newText
 
         if (animateTextChange) {
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
index 3f02eba..cd3e363 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
@@ -15,8 +15,10 @@
  */
 package com.android.permissioncontroller.safetycenter.ui;
 
+import static android.content.Intent.ACTION_SAFETY_CENTER;
 import static android.content.Intent.FLAG_ACTIVITY_FORWARD_RESULT;
 import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCES_GROUP_ID;
 
 import static com.android.permissioncontroller.PermissionControllerStatsLog.PRIVACY_SIGNAL_NOTIFICATION_INTERACTION;
 import static com.android.permissioncontroller.PermissionControllerStatsLog.PRIVACY_SIGNAL_NOTIFICATION_INTERACTION__ACTION__NOTIFICATION_CLICKED;
@@ -27,12 +29,14 @@
 import android.safetycenter.SafetyCenterManager;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.fragment.app.Fragment;
 
 import com.android.permissioncontroller.Constants;
 import com.android.permissioncontroller.PermissionControllerStatsLog;
 import com.android.permissioncontroller.R;
+import com.android.settingslib.activityembedding.ActivityEmbeddingUtils;
 import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
 
 /** Entry-point activity for SafetyCenter. */
@@ -41,6 +45,12 @@
 
     private static final String TAG = SafetyCenterActivity.class.getSimpleName();
     private static final String PRIVACY_CONTROLS_ACTION = "android.settings.PRIVACY_CONTROLS";
+    private static final String MENU_KEY_SAFETY_CENTER = "top_level_safety_center";
+    private static final String EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI =
+            "android.provider.extra.SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI";
+    private static final String EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY =
+            "android.provider.extra.SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY";
+
     private SafetyCenterManager mSafetyCenterManager;
 
     @Override
@@ -48,10 +58,22 @@
         super.onCreate(savedInstanceState);
         mSafetyCenterManager = getSystemService(SafetyCenterManager.class);
 
-        if (maybeRedirectIfDisabled()) return;
+        if (maybeRedirectIfDisabled()) {
+            return;
+        }
+
+        if (maybeRedirectIntoTwoPaneSettings()) {
+            return;
+        }
 
         Fragment frag;
-        if (getIntent().getAction().equals(PRIVACY_CONTROLS_ACTION)) {
+        if (SafetyCenterUiFlags.getShowSubpages()
+                && getIntent().getAction().equals(ACTION_SAFETY_CENTER)
+                && getIntent().hasExtra(EXTRA_SAFETY_SOURCES_GROUP_ID)) {
+            frag =
+                    new SafetyCenterSubpageFragment(
+                            getIntent().getStringExtra(EXTRA_SAFETY_SOURCES_GROUP_ID));
+        } else if (getIntent().getAction().equals(PRIVACY_CONTROLS_ACTION)) {
             setTitle(R.string.privacy_controls_title);
             frag = PrivacyControlsFragment.newInstance();
         } else {
@@ -59,12 +81,19 @@
             setTitle(getString(R.string.safety_center_dashboard_page_title));
             frag = new SafetyCenterScrollWrapperFragment();
         }
+
         if (savedInstanceState == null) {
             getSupportFragmentManager()
                     .beginTransaction()
                     .add(R.id.content_frame, frag)
                     .commitNow();
         }
+
+        if (getActionBar() != null
+                && ActivityEmbeddingUtils.shouldHideNavigateUpButton(this, true)) {
+            getActionBar().setDisplayHomeAsUpEnabled(false);
+            getActionBar().setHomeButtonEnabled(false);
+        }
     }
 
     @Override
@@ -84,6 +113,46 @@
         return false;
     }
 
+    private boolean maybeRedirectIntoTwoPaneSettings() {
+        return shouldUseTwoPaneSettings() && tryRedirectTwoPaneSettings();
+    }
+
+    private boolean shouldUseTwoPaneSettings() {
+        if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this)) {
+            return false;
+        }
+        return isTaskRoot() && !ActivityEmbeddingUtils.isActivityEmbedded(this);
+    }
+
+    /** Return {@code true} if the redirection was attempted. */
+    private boolean tryRedirectTwoPaneSettings() {
+        Intent twoPaneIntent = getTwoPaneIntent();
+        if (twoPaneIntent == null) {
+            return false;
+        }
+
+        Log.i(TAG, "Safety Center restarting in Settings two-pane layout");
+        startActivity(twoPaneIntent);
+        finishAndRemoveTask();
+        return true;
+    }
+
+    @Nullable
+    private Intent getTwoPaneIntent() {
+        Intent twoPaneIntent = ActivityEmbeddingUtils.buildEmbeddingActivityBaseIntent(this);
+        if (twoPaneIntent == null) {
+            return null;
+        }
+
+        twoPaneIntent.putExtras(getIntent());
+        twoPaneIntent.putExtra(
+                EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI,
+                getIntent().toUri(Intent.URI_INTENT_SCHEME));
+        twoPaneIntent.putExtra(
+                EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY, MENU_KEY_SAFETY_CENTER);
+        return twoPaneIntent;
+    }
+
     private void logPrivacySourceMetric() {
         Intent intent = getIntent();
         if (intent != null && intent.hasExtra(Constants.EXTRA_PRIVACY_SOURCE)) {
@@ -91,8 +160,14 @@
             int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
             long sessionId =
                     intent.getLongExtra(Constants.EXTRA_SESSION_ID, Constants.INVALID_SESSION_ID);
-            Log.v(TAG, "privacy source notification metric, source " + privacySource + " uid "
-                    + uid + " sessionId " + sessionId);
+            Log.v(
+                    TAG,
+                    "privacy source notification metric, source "
+                            + privacySource
+                            + " uid "
+                            + uid
+                            + " sessionId "
+                            + sessionId);
             PermissionControllerStatsLog.write(
                     PRIVACY_SIGNAL_NOTIFICATION_INTERACTION,
                     privacySource,
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
index 92001b5..ce70d0f 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
@@ -38,7 +38,6 @@
 import android.safetycenter.SafetyCenterIssue;
 import android.safetycenter.SafetyCenterStaticEntry;
 import android.safetycenter.SafetyCenterStaticEntryGroup;
-import android.safetycenter.SafetyCenterStatus;
 import android.util.Log;
 import android.widget.Toast;
 
@@ -58,6 +57,7 @@
 import com.android.permissioncontroller.safetycenter.ui.model.LiveSafetyCenterViewModelFactory;
 import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData;
 import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel;
+import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData;
 import com.android.safetycenter.internaldata.SafetyCenterIds;
 import com.android.safetycenter.resources.SafetyCenterResourcesContext;
 
@@ -174,11 +174,6 @@
 
         mSafetyStatusPreference =
                 requireNonNull(getPreferenceScreen().findPreference(SAFETY_STATUS_KEY));
-        // TODO: Use real strings here, or set more sensible defaults in the layout
-        mSafetyStatusPreference.setSafetyStatus(
-                new SafetyCenterStatus.Builder("Looks good", "")
-                        .setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN)
-                        .build());
         mSafetyStatusPreference.setViewModel(mViewModel);
 
         mIssuesGroup = getPreferenceScreen().findPreference(ISSUES_GROUP_KEY);
@@ -191,6 +186,7 @@
             mStaticEntriesGroup = null;
         }
 
+        mViewModel.getStatusUiLiveData().observe(this, this::updateStatus);
         mViewModel.getSafetyCenterUiLiveData().observe(this, this::renderSafetyCenterData);
         mViewModel.getErrorLiveData().observe(this, this::displayErrorDetails);
 
@@ -256,6 +252,21 @@
         return mViewModel;
     }
 
+    private void updateStatus(StatusUiData statusUiData) {
+        if (mIsQuickSettingsFragment) {
+            SafetyCenterResourcesContext safetyCenterResourcesContext =
+                    new SafetyCenterResourcesContext(requireContext());
+            boolean hasPendingActions =
+                    safetyCenterResourcesContext
+                            .getStringByName("overall_severity_level_ok_review_summary")
+                            .equals(statusUiData.getOriginalSummary().toString());
+
+            statusUiData = statusUiData.copyForPendingActions(hasPendingActions);
+        }
+
+        mSafetyStatusPreference.setData(statusUiData);
+    }
+
     private void renderSafetyCenterData(@Nullable SafetyCenterUiData uiData) {
         if (uiData == null) return;
         SafetyCenterData data = uiData.getSafetyCenterData();
@@ -267,37 +278,13 @@
             return;
         }
 
-        mSafetyStatusPreference.setSafetyData(data);
-
         // TODO(b/208212820): Only update entries that have changed since last
         // update, rather than deleting and re-adding all.
         updateIssues(context, data.getIssues(), uiData.getResolvedIssues());
+
         if (!mIsQuickSettingsFragment) {
             updateSafetyEntries(context, data.getEntriesOrGroups());
             updateStaticSafetyEntries(context, data.getStaticEntryGroups());
-        } else {
-            SafetyCenterResourcesContext safetyCenterResourcesContext =
-                    new SafetyCenterResourcesContext(context);
-            boolean hasSettingsToReview =
-                    safetyCenterResourcesContext
-                            .getStringByName("overall_severity_level_ok_review_summary")
-                            .equals(data.getStatus().getSummary().toString());
-            setPendingActionState(hasSettingsToReview);
-        }
-    }
-
-    /** Determine if there are pending actions and set pending actions state */
-    private void setPendingActionState(boolean hasSettingsToReview) {
-        if (hasSettingsToReview) {
-            mSafetyStatusPreference.setHasPendingActions(
-                    true,
-                    l -> {
-                        mViewModel.navigateToSafetyCenter(
-                                this, NavigationSource.QUICK_SETTINGS_TILE);
-                        mViewModel.getInteractionLogger().record(Action.REVIEW_SETTINGS_CLICKED);
-                    });
-        } else {
-            mSafetyStatusPreference.setHasPendingActions(false, null);
         }
     }
 
@@ -334,7 +321,9 @@
             boolean isFirstElement = i == 0;
             boolean isLastElement = i == size - 1;
 
-            if (entry != null) {
+            if (SafetyCenterUiFlags.getShowSubpages() && group != null) {
+                mEntriesGroup.addPreference(new SafetyHomepageEntryPreference(context, group));
+            } else if (entry != null) {
                 addTopLevelEntry(context, entry, isFirstElement, isLastElement);
             } else if (group != null) {
                 addGroupEntries(context, group, isFirstElement, isLastElement);
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java
index ea3146c..d6015ff 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java
@@ -174,22 +174,24 @@
         securitySettings.setOnClickListener(
                 (v) ->
                         mSafetyCenterViewModel.navigateToSafetyCenter(
-                                this, NavigationSource.QUICK_SETTINGS_TILE));
-        TextView securitySettingsText =
-                securitySettings.findViewById(R.id.toggle_sensor_name);
+                                mContext, NavigationSource.QUICK_SETTINGS_TILE));
+        TextView securitySettingsText = securitySettings.findViewById(R.id.toggle_sensor_name);
         securitySettingsText.setText(R.string.settings);
         securitySettingsText.setSelected(true);
         securitySettings.findViewById(R.id.toggle_sensor_status).setVisibility(View.GONE);
-        ImageView securitySettingsIcon =
-                securitySettings.findViewById(R.id.toggle_sensor_icon);
-        securitySettingsIcon.setImageDrawable(Utils.applyTint(mContext,
-                mContext.getDrawable(R.drawable.ic_safety_center_shield),
-                android.R.attr.textColorPrimaryInverse));
+        ImageView securitySettingsIcon = securitySettings.findViewById(R.id.toggle_sensor_icon);
+        securitySettingsIcon.setImageDrawable(
+                Utils.applyTint(
+                        mContext,
+                        mContext.getDrawable(R.drawable.ic_safety_center_shield),
+                        android.R.attr.textColorPrimaryInverse));
         securitySettings.findViewById(R.id.arrow_icon).setVisibility(View.VISIBLE);
         ((ImageView) securitySettings.findViewById(R.id.arrow_icon))
-                .setImageDrawable(Utils.applyTint(mContext,
-                        mContext.getDrawable(R.drawable.ic_chevron_right),
-                        android.R.attr.textColorSecondaryInverse));
+                .setImageDrawable(
+                        Utils.applyTint(
+                                mContext,
+                                mContext.getDrawable(R.drawable.ic_chevron_right),
+                                android.R.attr.textColorSecondaryInverse));
         ViewCompat.replaceAccessibilityAction(
                 securitySettings,
                 AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK,
@@ -218,11 +220,14 @@
             return;
         }
         View permissionSectionTitleView = rootView.findViewById(R.id.permission_section_title);
+        View statusSectionTitleView = rootView.findViewById(R.id.status_section_title);
         if (mPermGroupUsages == null || mPermGroupUsages.isEmpty()) {
             permissionSectionTitleView.setVisibility(View.GONE);
+            statusSectionTitleView.setVisibility(View.GONE);
             return;
         }
         permissionSectionTitleView.setVisibility(View.VISIBLE);
+        statusSectionTitleView.setVisibility(View.VISIBLE);
         LinearLayout usageLayout = rootView.findViewById(R.id.permission_usage);
         Collections.sort(
                 mPermGroupUsages,
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt
new file mode 100644
index 0000000..1ac23e4
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.safetycenter.ui
+
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+import android.os.Bundle
+import android.safetycenter.SafetyCenterEntryGroup
+import android.safetycenter.SafetyCenterEntryOrGroup
+import android.safetycenter.SafetyCenterErrorDetails
+import android.util.Log
+import android.widget.Toast
+import androidx.annotation.RequiresApi
+import androidx.lifecycle.ViewModelProvider
+import androidx.preference.PreferenceFragmentCompat
+import androidx.preference.PreferenceGroup
+import androidx.preference.PreferenceScreen
+import androidx.recyclerview.widget.RecyclerView
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.safetycenter.ui.model.LiveSafetyCenterViewModelFactory
+import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData
+import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel
+import com.android.safetycenter.internaldata.SafetyCenterIds
+import com.android.safetycenter.resources.SafetyCenterResourcesContext
+
+/** A fragment that represents a generic subpage in Safety Center. */
+@RequiresApi(UPSIDE_DOWN_CAKE)
+class SafetyCenterSubpageFragment(private val sourceGroupId: String) : PreferenceFragmentCompat() {
+
+    private lateinit var viewModel: SafetyCenterViewModel
+    private lateinit var sameTaskSourceIds: List<String>
+    private var subpageEntryGroup: PreferenceGroup? = null
+
+    override fun onCreateAdapter(
+        preferenceScreen: PreferenceScreen?
+    ): RecyclerView.Adapter<RecyclerView.ViewHolder> {
+        /* By default, the PreferenceGroupAdapter does setHasStableIds(true). Since each Preference
+         * is internally allocated with an auto-incremented ID, it does not allow us to gracefully
+         * update only changed preferences based on SafetyPreferenceComparisonCallback. In order to
+         * allow the list to track the changes, we need to ignore the Preference IDs. */
+        val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> =
+            super.onCreateAdapter(preferenceScreen)
+        adapter.setHasStableIds(false)
+        return adapter
+    }
+
+    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+        setPreferencesFromResource(R.xml.safety_center_subpage, rootKey)
+        sameTaskSourceIds =
+            SafetyCenterResourcesContext(requireContext())
+                .getStringByName("config_same_task_safety_source_ids")
+                .split(",")
+        subpageEntryGroup = getPreferenceScreen().findPreference(ENTRY_GROUP_KEY)
+
+        viewModel =
+            ViewModelProvider(
+                    requireActivity(),
+                    LiveSafetyCenterViewModelFactory(requireActivity().getApplication()))
+                .get(SafetyCenterViewModel::class.java)
+
+        viewModel.safetyCenterUiLiveData.observe(this) { uiData: SafetyCenterUiData? ->
+            renderSafetyCenterEntryGroup(uiData)
+        }
+        viewModel.errorLiveData.observe(this) { errorDetails: SafetyCenterErrorDetails? ->
+            displayErrorDetails(errorDetails)
+        }
+
+        getPreferenceManager().setPreferenceComparisonCallback(SafetyPreferenceComparisonCallback())
+    }
+
+    override fun onResume() {
+        super.onResume()
+        // TODO(b/253168600): Replace with subpage specific refresh
+        viewModel.pageOpen()
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        if (activity?.isChangingConfigurations == true) {
+            viewModel.changingConfigurations()
+        }
+    }
+
+    private fun displayErrorDetails(errorDetails: SafetyCenterErrorDetails?) {
+        if (errorDetails == null) return
+        Toast.makeText(requireContext(), errorDetails.errorMessage, Toast.LENGTH_LONG).show()
+        viewModel.clearError()
+    }
+
+    private fun renderSafetyCenterEntryGroup(uiData: SafetyCenterUiData?) {
+        Log.d(TAG, "renderSafetyCenterEntryGroup called with $uiData")
+        val entryGroup = getMatchingGroup(uiData, sourceGroupId)
+        if (entryGroup == null) {
+            Log.w(TAG, "$sourceGroupId doesn't match any of the existing SafetySourcesGroup IDs")
+            requireActivity().getSupportFragmentManager().popBackStack()
+            return
+        }
+
+        requireActivity().setTitle(entryGroup.title)
+        updateSafetyCenterEntries(entryGroup)
+    }
+
+    private fun getMatchingGroup(
+        uiData: SafetyCenterUiData?,
+        sourceGroupId: String
+    ): SafetyCenterEntryGroup? {
+        val entryOrGroups: List<SafetyCenterEntryOrGroup>? =
+            uiData?.safetyCenterData?.entriesOrGroups
+        val entryGroups = entryOrGroups?.mapNotNull { it.entryGroup }
+        return entryGroups?.find { it.id == sourceGroupId }
+    }
+
+    private fun updateSafetyCenterEntries(entryGroup: SafetyCenterEntryGroup) {
+        Log.d(TAG, "updateSafetyCenterEntries called with $entryGroup")
+        subpageEntryGroup?.removeAll()
+        for (entry in entryGroup.entries) {
+            subpageEntryGroup?.addPreference(
+                SafetySubpageEntryPreference(requireContext(), getTaskIdForEntry(entry.id), entry))
+        }
+    }
+
+    private fun getTaskIdForEntry(entryId: String): Int? {
+        val sourceId: String = SafetyCenterIds.entryIdFromString(entryId).getSafetySourceId()
+        return if (sameTaskSourceIds.contains(sourceId)) requireActivity().getTaskId() else null
+    }
+
+    companion object {
+        private val TAG: String = SafetyCenterSubpageFragment::class.java.simpleName
+        private const val ENTRY_GROUP_KEY: String = "subpage_entry_group"
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterUiFlags.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterUiFlags.kt
new file mode 100644
index 0000000..053a777
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterUiFlags.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.safetycenter.ui
+
+import android.provider.DeviceConfig
+import androidx.core.os.BuildCompat
+
+/** A class to access the Safety Center UI related {@link DeviceConfig} flags. */
+object SafetyCenterUiFlags {
+    private const val PROPERTY_SHOW_SUBPAGES = "safety_center_show_subpages"
+
+    /**
+     * Returns whether to show subpages in the Safety Center UI for Android-U instead of the
+     * expand-and-collapse list implementation.
+     */
+    @JvmStatic
+    fun getShowSubpages(): Boolean {
+        return BuildCompat.isAtLeastU() &&
+            DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_SHOW_SUBPAGES, false)
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt
index 78837dd..75e8800 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyGroupPreference.kt
@@ -27,9 +27,7 @@
 import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel
 import com.android.permissioncontroller.safetycenter.ui.view.SafetyEntryGroupView
 
-/**
- * A preference that displays a visual representation of a {@link SafetyCenterEntryGroup}.
- */
+/** A preference that displays a visual representation of a {@link SafetyCenterEntryGroup}. */
 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
 class SafetyGroupPreference(
     context: Context,
@@ -43,8 +41,6 @@
     private val onCollapsedListener: (String) -> Unit
 ) : Preference(context), ComparablePreference {
 
-    val groupId = group.id
-
     init {
         layoutResource = R.layout.preference_group
     }
@@ -53,24 +49,22 @@
         super.onBindViewHolder(holder)
 
         (holder?.itemView as? SafetyEntryGroupView)?.showGroup(
-                group,
-                isExpanded,
-                isFirstCard,
-                isLastCard,
-                getTaskIdForEntry,
-                viewModel,
-                onExpandedListener,
-                onCollapsedListener
-        )
+            group,
+            isExpanded,
+            isFirstCard,
+            isLastCard,
+            getTaskIdForEntry,
+            viewModel,
+            onExpandedListener,
+            onCollapsedListener)
     }
 
     override fun isSameItem(other: Preference): Boolean =
-            other is SafetyGroupPreference &&
-                    TextUtils.equals(group.id, other.group.id)
+        other is SafetyGroupPreference && TextUtils.equals(group.id, other.group.id)
 
     override fun hasSameContents(other: Preference): Boolean =
-            other is SafetyGroupPreference &&
-                    group == other.group &&
-                    isFirstCard == other.isFirstCard &&
-                    isLastCard == other.isLastCard
-}
\ No newline at end of file
+        other is SafetyGroupPreference &&
+            group == other.group &&
+            isFirstCard == other.isFirstCard &&
+            isLastCard == other.isLastCard
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyHomepageEntryPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyHomepageEntryPreference.kt
new file mode 100644
index 0000000..583f3f3
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyHomepageEntryPreference.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.safetycenter.ui
+
+import android.content.Context
+import android.content.Intent
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+import android.safetycenter.SafetyCenterEntryGroup
+import android.safetycenter.SafetyCenterManager
+import android.text.TextUtils
+import androidx.annotation.RequiresApi
+import androidx.preference.Preference
+import java.util.Objects
+
+/**
+ * A preference that displays a visual representation of a {@link SafetyCenterEntryGroup} on the
+ * Safety Center homepage.
+ */
+@RequiresApi(UPSIDE_DOWN_CAKE)
+internal class SafetyHomepageEntryPreference(
+    context: Context,
+    private val entryGroup: SafetyCenterEntryGroup
+) : Preference(context), ComparablePreference {
+
+    init {
+        setTitle(entryGroup.title)
+        setSummary(entryGroup.summary)
+        setIcon(
+            SeverityIconPicker.selectIconResId(
+                entryGroup.severityLevel, entryGroup.severityUnspecifiedIconType))
+
+        // TODO(b/260822348): Check if there is a better way to open the subpage fragment
+        val intent = Intent(Intent.ACTION_SAFETY_CENTER)
+        intent.putExtra(SafetyCenterManager.EXTRA_SAFETY_SOURCES_GROUP_ID, entryGroup.id)
+        setIntent(intent)
+    }
+
+    override fun isSameItem(preference: Preference): Boolean =
+        preference is SafetyHomepageEntryPreference && entryGroup.id == preference.entryGroup.id
+
+    override fun hasSameContents(preference: Preference): Boolean =
+        preference is SafetyHomepageEntryPreference &&
+            Objects.equals(entryGroup.id, preference.entryGroup.id) &&
+            TextUtils.equals(entryGroup.title, preference.entryGroup.title) &&
+            TextUtils.equals(entryGroup.summary, preference.entryGroup.summary) &&
+            entryGroup.severityLevel == preference.entryGroup.severityLevel &&
+            entryGroup.severityUnspecifiedIconType ==
+                preference.entryGroup.severityUnspecifiedIconType
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java
index f081017..ee2e7c7 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java
@@ -17,9 +17,6 @@
 package com.android.permissioncontroller.safetycenter.ui;
 
 import static android.os.Build.VERSION_CODES.TIRAMISU;
-import static android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING;
-import static android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK;
-import static android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION;
 import static android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN;
 
 import android.content.Context;
@@ -28,11 +25,9 @@
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Looper;
-import android.safetycenter.SafetyCenterData;
 import android.safetycenter.SafetyCenterStatus;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -46,6 +41,7 @@
 import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.permission.utils.KotlinUtils;
 import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel;
+import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData;
 
 import com.google.android.material.button.MaterialButton;
 
@@ -57,10 +53,8 @@
 /** Preference which displays a visual representation of {@link SafetyCenterStatus}. */
 @RequiresApi(TIRAMISU)
 public class SafetyStatusPreference extends Preference implements ComparablePreference {
-    private static final String TAG = "SafetyStatusPreference";
 
-    @Nullable private SafetyCenterStatus mStatus;
-    @Nullable private View.OnClickListener mReviewSettingsOnClickListener;
+    @Nullable private StatusUiData mStatus;
     @Nullable private SafetyCenterViewModel mViewModel;
 
     @NonNull
@@ -74,8 +68,6 @@
             new TextFadeAnimator(List.of(R.id.status_title, R.id.status_summary));
 
     private boolean mFirstBind = true;
-    private boolean mHasPendingActions;
-    private boolean mHasIssues;
 
     public SafetyStatusPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -103,8 +95,16 @@
         MaterialButton pendingActionsRescanButton =
                 (MaterialButton) holder.findViewById(R.id.pending_actions_rescan_button);
         View reviewSettingsButton = holder.findViewById(R.id.review_settings_button);
-        if (mHasPendingActions) {
-            reviewSettingsButton.setOnClickListener(mReviewSettingsOnClickListener);
+        if (mStatus.hasPendingActions()) {
+            reviewSettingsButton.setOnClickListener(
+                    l -> {
+                        requireViewModel()
+                                .navigateToSafetyCenter(
+                                        context, NavigationSource.QUICK_SETTINGS_TILE);
+                        requireViewModel()
+                                .getInteractionLogger()
+                                .record(Action.REVIEW_SETTINGS_CLICKED);
+                    });
             reviewSettingsButton.setVisibility(View.VISIBLE);
         } else {
             reviewSettingsButton.setVisibility(View.GONE);
@@ -112,15 +112,8 @@
         rescanButton = updateRescanButtonUi(rescanButton, pendingActionsRescanButton);
         setRescanButtonState(rescanButton);
 
-        int contentDescriptionResId =
-                R.string.safety_status_preference_title_and_summary_content_description;
         holder.findViewById(R.id.status_title_and_summary)
-                .setContentDescription(
-                        getContext()
-                                .getString(
-                                        contentDescriptionResId,
-                                        mStatus.getTitle(),
-                                        mStatus.getSummary()));
+                .setContentDescription(mStatus.getContentDescription(context));
 
         rescanButton.setOnClickListener(
                 unused -> {
@@ -143,7 +136,8 @@
         View safetyProtectionSectionView = holder.findViewById(R.id.safety_protection_section_view);
         if (KotlinUtils.INSTANCE.shouldShowSafetyProtectionResources(context)) {
             // Hide the Safety Protection branding if there are any issue cards
-            safetyProtectionSectionView.setVisibility(mHasIssues ? View.GONE : View.VISIBLE);
+            safetyProtectionSectionView.setVisibility(
+                    mStatus.hasIssues() ? View.GONE : View.VISIBLE);
         }
         if (safetyProtectionSectionView.getVisibility() == View.GONE) {
             holder.itemView.setPaddingRelative(
@@ -165,7 +159,7 @@
     private void updateStatusText(TextView title, TextView summary) {
         if (mFirstBind) {
             title.setText(mStatus.getTitle());
-            summary.setText(getSummaryText());
+            summary.setText(mStatus.getSummary(getContext()));
         }
         runTextAnimationIfNeeded(title, summary);
     }
@@ -173,7 +167,7 @@
     private void updateStatusIcon(ImageView statusImage, View rescanButton) {
         int severityLevel = mStatus.getSeverityLevel();
 
-        boolean isRefreshing = isRefreshInProgress();
+        boolean isRefreshing = mStatus.isRefreshInProgress();
         boolean shouldStartScanAnimation = isRefreshing && !mIsScanAnimationRunning;
         boolean shouldEndScanAnimation = !isRefreshing && mIsScanAnimationRunning;
         boolean shouldChangeIcon = mSettledSeverityLevel != severityLevel;
@@ -202,13 +196,14 @@
             return;
         }
         String titleText = mStatus.getTitle().toString();
-        String summaryText = getSummaryText().toString();
+        String summaryText = mStatus.getSummary(getContext()).toString();
         boolean titleEquals = titleView.getText().toString().equals(titleText);
         boolean summaryEquals = summaryView.getText().toString().equals(summaryText);
-        Runnable  -> {
-            mIsTextChangeAnimationRunning = false;
-            runTextAnimationIfNeeded(titleView, summaryView);
-        };
+        Runnable >
+                () -> {
+                    mIsTextChangeAnimationRunning = false;
+                    runTextAnimationIfNeeded(titleView, summaryView);
+                };
         mIsTextChangeAnimationRunning = !titleEquals || !summaryEquals;
         if (!titleEquals && !summaryEquals) {
             Pair<TextView, String> titleChange = new Pair<>(titleView, titleText);
@@ -221,20 +216,6 @@
         }
     }
 
-    private CharSequence getSummaryText() {
-        if (mHasPendingActions) {
-            return getContext().getString(R.string.safety_center_qs_status_summary);
-        } else {
-            return mStatus.getSummary().toString();
-        }
-    }
-
-    private boolean isRefreshInProgress() {
-        int refreshStatus = mStatus.getRefreshStatus();
-        return refreshStatus == SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS
-                || refreshStatus == SafetyCenterStatus.REFRESH_STATUS_DATA_FETCH_IN_PROGRESS;
-    }
-
     private void startScanningAnimation(ImageView statusImage) {
         mIsScanAnimationRunning = true;
         statusImage.setImageResource(
@@ -264,7 +245,7 @@
                 new Animatable2.AnimationCallback() {
                     @Override
                     public void onAnimationEnd(Drawable drawable) {
-                        if (mIsScanAnimationRunning && isRefreshInProgress()) {
+                        if (mIsScanAnimationRunning && mStatus.isRefreshInProgress()) {
                             scanningAnim.start();
                         } else {
                             scanningAnim.clearAnimationCallbacks();
@@ -303,8 +284,7 @@
                                     @Override
                                     public void onAnimationEnd(Drawable drawable) {
                                         super.onAnimationEnd(drawable);
-                                        finishScanAnimation(
-                                                statusImage, rescanButton);
+                                        finishScanAnimation(statusImage, rescanButton);
                                     }
                                 });
                         animatedDrawable.start();
@@ -350,7 +330,7 @@
         }
 
         mSettledSeverityLevel = mStatus.getSeverityLevel();
-        statusImage.setImageResource(toStatusImageResId(mSettledSeverityLevel));
+        statusImage.setImageResource(mStatus.getStatusImageResId());
     }
 
     private void handleQueuedAction(ImageView statusImage) {
@@ -369,7 +349,7 @@
      */
     private MaterialButton updateRescanButtonUi(
             MaterialButton rescanButton, MaterialButton pendingActionsRescanButton) {
-        if (mHasPendingActions) {
+        if (mStatus.hasPendingActions()) {
             rescanButton.setVisibility(View.GONE);
             pendingActionsRescanButton.setVisibility(View.VISIBLE);
             return pendingActionsRescanButton;
@@ -379,14 +359,8 @@
         return rescanButton;
     }
 
-    void setSafetyStatus(SafetyCenterStatus status) {
-        mStatus = status;
-        safeNotifyChanged();
-    }
-
-    void setSafetyData(SafetyCenterData data) {
-        mHasIssues = data.getIssues().size() > 0;
-        mStatus = data.getStatus();
+    void setData(StatusUiData statusUiData) {
+        mStatus = statusUiData;
         safeNotifyChanged();
     }
 
@@ -398,50 +372,15 @@
         return Objects.requireNonNull(mViewModel);
     }
 
-    /**
-     * System has pending actions when the user security and privacy signals are deemed to be safe,
-     * but the user has previously dismissed some warnings that may need their review
-     */
-    void setHasPendingActions(boolean hasPendingActions, View.OnClickListener listener) {
-        mHasPendingActions = hasPendingActions;
-        mReviewSettingsOnClickListener = listener;
-        safeNotifyChanged();
-    }
-
     private void setRescanButtonState(View rescanButton) {
-        rescanButton.setVisibility(shouldShowRescanButton() ? View.VISIBLE : View.GONE);
-        rescanButton.setEnabled(!isRefreshInProgress());
-    }
-
-    private boolean shouldShowRescanButton() {
-        int severityLevel = mStatus.getSeverityLevel();
-        return !mHasIssues
-                && !mHasPendingActions // hides the second button in QS to keep the UI clean
-                && (severityLevel == OVERALL_SEVERITY_LEVEL_OK
-                        || severityLevel == OVERALL_SEVERITY_LEVEL_UNKNOWN);
+        rescanButton.setVisibility(mStatus.shouldShowRescanButton() ? View.VISIBLE : View.GONE);
+        rescanButton.setEnabled(!mStatus.isRefreshInProgress());
     }
 
     // Calling notifyChanged while recyclerview is scrolling or computing layout will result in an
     // IllegalStateException. Post to handler to wait for UI to settle.
     private void safeNotifyChanged() {
-        new Handler(Looper.getMainLooper()).post(() -> notifyChanged());
-    }
-
-    private static int toStatusImageResId(int overallSeverityLevel) {
-        switch (overallSeverityLevel) {
-            case OVERALL_SEVERITY_LEVEL_UNKNOWN:
-            case OVERALL_SEVERITY_LEVEL_OK:
-                return R.drawable.safety_status_info;
-            case OVERALL_SEVERITY_LEVEL_RECOMMENDATION:
-                return R.drawable.safety_status_recommendation;
-            case OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING:
-                return R.drawable.safety_status_warn;
-            default:
-                Log.w(
-                        TAG,
-                        String.format("Unexpected OverallSeverityLevel: %s", overallSeverityLevel));
-                return R.drawable.safety_status_info;
-        }
+        new Handler(Looper.getMainLooper()).post(this::notifyChanged);
     }
 
     @Override
@@ -456,6 +395,6 @@
             return false;
         }
         SafetyStatusPreference other = (SafetyStatusPreference) preference;
-        return Objects.equals(mStatus, other.mStatus) && mHasIssues == other.mHasIssues;
+        return Objects.equals(mStatus, other.mStatus);
     }
 }
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetySubpageEntryPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetySubpageEntryPreference.kt
new file mode 100644
index 0000000..3a7b462
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetySubpageEntryPreference.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.safetycenter.ui
+
+import android.content.Context
+import android.os.Build
+import android.safetycenter.SafetyCenterEntry
+import android.safetycenter.SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR
+import android.text.TextUtils
+import android.util.Log
+import android.widget.ImageView
+import androidx.annotation.RequiresApi
+import androidx.preference.Preference
+import androidx.preference.PreferenceViewHolder
+import com.android.permissioncontroller.R
+import com.android.settingslib.widget.TwoTargetPreference
+
+/**
+ * A preference that displays a visual representation of a {@link SafetyCenterEntry} on the Safety
+ * Center subpage.
+ */
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+class SafetySubpageEntryPreference(
+    context: Context,
+    private val launchTaskId: Int?,
+    private val entry: SafetyCenterEntry
+) : TwoTargetPreference(context), ComparablePreference {
+
+    init {
+        setupIconActionButton()
+        setupClickListener()
+        setTitle(entry.title)
+        setSummary(entry.summary)
+        setSelectable(true)
+    }
+
+    private fun setupIconActionButton() {
+        if (entry.iconAction != null) {
+            setIconSize(ICON_SIZE_MEDIUM)
+            setWidgetLayoutResource(
+                if (entry.iconAction!!.type == ICON_ACTION_TYPE_GEAR) {
+                    R.layout.preference_entry_icon_action_gear_widget
+                } else {
+                    R.layout.preference_entry_icon_action_info_widget
+                })
+        }
+    }
+
+    private fun setupClickListener() {
+        val pendingIntent = entry.pendingIntent
+        if (pendingIntent != null) {
+            setOnPreferenceClickListener {
+                try {
+                    PendingIntentSender.send(pendingIntent, launchTaskId)
+                    true
+                } catch (ex: Exception) {
+                    Log.e(TAG, "Failed to execute pending intent for $entry", ex)
+                    false
+                }
+            }
+        } else {
+            Log.w(TAG, "Pending intent is null for $entry")
+        }
+    }
+
+    override fun onBindViewHolder(holder: PreferenceViewHolder) {
+        super.onBindViewHolder(holder)
+        val iconAction = entry.iconAction
+        if (iconAction == null) {
+            Log.w(TAG, "Icon action is null for $entry")
+        } else {
+            val iconActionButton = holder.findViewById(R.id.icon_action_button) as? ImageView?
+            iconActionButton?.setOnClickListener {
+                try {
+                    PendingIntentSender.send(iconAction.pendingIntent, launchTaskId)
+                } catch (ex: Exception) {
+                    Log.e(TAG, "Failed to execute icon action intent for $entry", ex)
+                }
+            }
+        }
+    }
+
+    override fun shouldHideSecondTarget(): Boolean = entry.iconAction == null
+
+    override fun isSameItem(preference: Preference): Boolean =
+        preference is SafetySubpageEntryPreference &&
+            TextUtils.equals(entry.id, preference.entry.id)
+
+    override fun hasSameContents(preference: Preference): Boolean =
+        preference is SafetySubpageEntryPreference && entry == preference.entry
+
+    companion object {
+        private val TAG: String = SafetySubpageEntryPreference::class.java.simpleName
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt
index c0d4e8f..9d2b808 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt
@@ -17,6 +17,7 @@
 package com.android.permissioncontroller.safetycenter.ui.model
 
 import android.app.Application
+import android.content.Context
 import android.content.Intent
 import android.content.Intent.ACTION_SAFETY_CENTER
 import android.os.Build
@@ -30,9 +31,9 @@
 import androidx.annotation.MainThread
 import androidx.annotation.RequiresApi
 import androidx.core.content.ContextCompat.getMainExecutor
-import androidx.fragment.app.Fragment
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.Transformations
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
 import com.android.permissioncontroller.safetycenter.ui.InteractionLogger
@@ -44,6 +45,8 @@
 class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) {
 
     private val TAG: String = LiveSafetyCenterViewModel::class.java.simpleName
+    override val statusUiLiveData: LiveData<StatusUiData>
+        get() = Transformations.map(safetyCenterUiLiveData) { StatusUiData(it.safetyCenterData) }
     override val safetyCenterUiLiveData: LiveData<SafetyCenterUiData> by this::_safetyCenterLiveData
     override val errorLiveData: LiveData<SafetyCenterErrorDetails> by this::_errorLiveData
 
@@ -117,14 +120,14 @@
         _errorLiveData.value = null
     }
 
-    override fun navigateToSafetyCenter(fragment: Fragment, navigationSource: NavigationSource?) {
+    override fun navigateToSafetyCenter(context: Context, navigationSource: NavigationSource?) {
         val intent = Intent(ACTION_SAFETY_CENTER)
 
         if (navigationSource != null) {
             navigationSource.addToIntent(intent)
         }
 
-        fragment.startActivity(intent)
+        context.startActivity(intent)
     }
 
     override fun pageOpen() {
@@ -219,9 +222,7 @@
         private fun determineResolvedIssues(nextIssueIds: Set<IssueId>): Map<IssueId, ActionId> {
             // Any previously in-flight issue that does not appear in the incoming SafetyCenterData
             // is considered resolved.
-            return issuesPendingResolution.filterNot { issue ->
-                nextIssueIds.contains(issue.key)
-            }
+            return issuesPendingResolution.filterNot { issue -> nextIssueIds.contains(issue.key) }
         }
 
         private fun shouldEndScan(nextData: SafetyCenterData): Boolean =
@@ -260,9 +261,7 @@
     }
 }
 
-/**
- * Returns inflight issues pending resolution
- */
+/** Returns inflight issues pending resolution */
 private fun SafetyCenterData.getInFlightIssues(): Map<IssueId, ActionId> =
     issues
         .map { issue ->
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt
index 291079c..d59c2b5 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt
@@ -17,12 +17,12 @@
 package com.android.permissioncontroller.safetycenter.ui.model
 
 import android.app.Application
+import android.content.Context
 import android.os.Build
 import android.safetycenter.SafetyCenterData
 import android.safetycenter.SafetyCenterErrorDetails
 import android.safetycenter.SafetyCenterIssue
 import androidx.annotation.RequiresApi
-import androidx.fragment.app.Fragment
 import androidx.lifecycle.AndroidViewModel
 import androidx.lifecycle.LiveData
 import com.android.permissioncontroller.safetycenter.ui.InteractionLogger
@@ -31,6 +31,7 @@
 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
 abstract class SafetyCenterViewModel(protected val app: Application) : AndroidViewModel(app) {
 
+    abstract val statusUiLiveData: LiveData<StatusUiData>
     abstract val safetyCenterUiLiveData: LiveData<SafetyCenterUiData>
     abstract val errorLiveData: LiveData<SafetyCenterErrorDetails>
     abstract val interactionLogger: InteractionLogger
@@ -56,7 +57,7 @@
     abstract fun clearError()
 
     abstract fun navigateToSafetyCenter(
-        fragment: Fragment,
+        context: Context,
         navigationSource: NavigationSource? = null
     )
 
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/StatusUiData.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/StatusUiData.kt
new file mode 100644
index 0000000..beeda21
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/StatusUiData.kt
@@ -0,0 +1,80 @@
+package com.android.permissioncontroller.safetycenter.ui.model
+
+import android.content.Context
+import android.safetycenter.SafetyCenterData
+import android.safetycenter.SafetyCenterStatus
+import android.util.Log
+import com.android.permissioncontroller.R
+
+/** UI model representation of a Status Card. */
+data class StatusUiData(
+    private val status: SafetyCenterStatus,
+    @get:JvmName("hasIssues") val hasIssues: Boolean = false,
+    @get:JvmName("hasPendingActions") val hasPendingActions: Boolean = false
+) {
+
+    constructor(
+        safetyCenterData: SafetyCenterData
+    ) : this(safetyCenterData.status, hasIssues = safetyCenterData.issues.size > 0)
+
+    // For convenience use in Java.
+    fun copyForPendingActions(hasPendingActions: Boolean) =
+        copy(hasPendingActions = hasPendingActions)
+
+    private companion object {
+        val TAG: String = StatusUiData::class.java.simpleName
+    }
+
+    val title: CharSequence by status::title
+    val originalSummary: CharSequence by status::summary
+    val severityLevel: Int by status::severityLevel
+
+    val statusImageResId: Int
+        get() =
+            when (severityLevel) {
+                SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN,
+                SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK -> R.drawable.safety_status_info
+                SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION ->
+                    R.drawable.safety_status_recommendation
+                SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING ->
+                    R.drawable.safety_status_warn
+                else -> {
+                    Log.w(TAG, "Unexpected OverallSeverityLevel: $severityLevel")
+                    R.drawable.safety_status_info
+                }
+            }
+
+    fun getSummary(context: Context): CharSequence {
+        return if (hasPendingActions) {
+            // Use a different string for the special quick-settings-only hasPendingActions state.
+            context.getString(R.string.safety_center_qs_status_summary)
+        } else {
+            originalSummary
+        }
+    }
+
+    fun getContentDescription(context: Context): CharSequence {
+        return context.getString(
+            R.string.safety_status_preference_title_and_summary_content_description,
+            title,
+            getSummary(context))
+    }
+
+    val isRefreshInProgress: Boolean
+        get() =
+            when (status.refreshStatus) {
+                SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS,
+                SafetyCenterStatus.REFRESH_STATUS_DATA_FETCH_IN_PROGRESS -> true
+                else -> false
+            }
+
+    fun shouldShowRescanButton(): Boolean {
+        return !hasIssues &&
+            !hasPendingActions &&
+            when (severityLevel) {
+                SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK,
+                SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN -> true
+                else -> false
+            }
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/AppOp.java b/PermissionController/src/com/android/role/controller/model/AppOp.java
similarity index 98%
rename from PermissionController/src/com/android/permissioncontroller/role/model/AppOp.java
rename to PermissionController/src/com/android/role/controller/model/AppOp.java
index 700cf7f..3156880 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/AppOp.java
+++ b/PermissionController/src/com/android/role/controller/model/AppOp.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/AppOpPermissions.java b/PermissionController/src/com/android/role/controller/model/AppOpPermissions.java
similarity index 98%
rename from PermissionController/src/com/android/permissioncontroller/role/model/AppOpPermissions.java
rename to PermissionController/src/com/android/role/controller/model/AppOpPermissions.java
index 25d8dc9..0f5e070 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/AppOpPermissions.java
+++ b/PermissionController/src/com/android/role/controller/model/AppOpPermissions.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.app.AppOpsManager;
 import android.content.Context;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/AssistantRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/AssistantRoleBehavior.java
similarity index 88%
rename from PermissionController/src/com/android/permissioncontroller/role/model/AssistantRoleBehavior.java
rename to PermissionController/src/com/android/role/controller/model/AssistantRoleBehavior.java
index 66dd8cc..70f9b1b 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/AssistantRoleBehavior.java
+++ b/PermissionController/src/com/android/role/controller/model/AssistantRoleBehavior.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.app.ActivityManager;
 import android.app.role.RoleManager;
@@ -27,7 +27,6 @@
 import android.content.res.XmlResourceParser;
 import android.os.Process;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.service.voice.VoiceInteractionService;
 import android.util.ArraySet;
 import android.util.AttributeSet;
@@ -37,7 +36,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.role.utils.UserUtils;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -78,33 +76,6 @@
         return !UserUtils.isProfile(user, context);
     }
 
-    @Override
-    public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
-            @NonNull Context context) {
-        return VisibilityMixin.isVisible("config_showDefaultAssistant", context);
-    }
-
-    @Nullable
-    @Override
-    public Intent getManageIntentAsUser(@NonNull Role role, @NonNull UserHandle user,
-            @NonNull Context context) {
-        boolean isAutomotive =
-                context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
-
-        if (isAutomotive) {
-            return null;
-        }
-
-        return new Intent(Settings.ACTION_VOICE_INPUT_SETTINGS);
-    }
-
-    @Nullable
-    @Override
-    public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName,
-            @NonNull Context context) {
-        return context.getString(R.string.assistant_confirmation_message);
-    }
-
     @Nullable
     @Override
     public List<String> getQualifyingPackagesAsUser(@NonNull Role role, @NonNull UserHandle user,
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/AutomotiveRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/AutomotiveRoleBehavior.java
similarity index 95%
rename from PermissionController/src/com/android/permissioncontroller/role/model/AutomotiveRoleBehavior.java
rename to PermissionController/src/com/android/role/controller/model/AutomotiveRoleBehavior.java
index 3596c7d..74187f2 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/AutomotiveRoleBehavior.java
+++ b/PermissionController/src/com/android/role/controller/model/AutomotiveRoleBehavior.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/BrowserRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/BrowserRoleBehavior.java
similarity index 94%
rename from PermissionController/src/com/android/permissioncontroller/role/model/BrowserRoleBehavior.java
rename to PermissionController/src/com/android/role/controller/model/BrowserRoleBehavior.java
index 1099835..adccfc6 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/BrowserRoleBehavior.java
+++ b/PermissionController/src/com/android/role/controller/model/BrowserRoleBehavior.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Context;
 import android.content.Intent;
@@ -29,7 +29,6 @@
 import androidx.annotation.Nullable;
 
 import com.android.modules.utils.build.SdkLevel;
-import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.permission.utils.CollectionUtils;
 import com.android.permissioncontroller.role.utils.PackageUtils;
 import com.android.permissioncontroller.role.utils.UserUtils;
@@ -151,10 +150,4 @@
             }
         }
     }
-
-    @Override
-    public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
-            @NonNull Context context) {
-        return context.getResources().getBoolean(R.bool.config_showBrowserRole);
-    }
 }
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/CompanionDeviceAppStreamingRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/CompanionDeviceAppStreamingRoleBehavior.java
similarity index 95%
rename from PermissionController/src/com/android/permissioncontroller/role/model/CompanionDeviceAppStreamingRoleBehavior.java
rename to PermissionController/src/com/android/role/controller/model/CompanionDeviceAppStreamingRoleBehavior.java
index ca4af23..b556a0d 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/CompanionDeviceAppStreamingRoleBehavior.java
+++ b/PermissionController/src/com/android/role/controller/model/CompanionDeviceAppStreamingRoleBehavior.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Context;
 
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/CompanionDeviceComputerRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/CompanionDeviceComputerRoleBehavior.java
similarity index 95%
rename from PermissionController/src/com/android/permissioncontroller/role/model/CompanionDeviceComputerRoleBehavior.java
rename to PermissionController/src/com/android/role/controller/model/CompanionDeviceComputerRoleBehavior.java
index 1d9409f..741a44e 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/CompanionDeviceComputerRoleBehavior.java
+++ b/PermissionController/src/com/android/role/controller/model/CompanionDeviceComputerRoleBehavior.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Context;
 
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/CompanionDeviceWatchRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/CompanionDeviceWatchRoleBehavior.java
similarity index 95%
rename from PermissionController/src/com/android/permissioncontroller/role/model/CompanionDeviceWatchRoleBehavior.java
rename to PermissionController/src/com/android/role/controller/model/CompanionDeviceWatchRoleBehavior.java
index 75675fb..1929422 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/CompanionDeviceWatchRoleBehavior.java
+++ b/PermissionController/src/com/android/role/controller/model/CompanionDeviceWatchRoleBehavior.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Context;
 
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/DevicePolicyManagementRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/DevicePolicyManagementRoleBehavior.java
similarity index 95%
rename from PermissionController/src/com/android/permissioncontroller/role/model/DevicePolicyManagementRoleBehavior.java
rename to PermissionController/src/com/android/role/controller/model/DevicePolicyManagementRoleBehavior.java
index 8232847..a423f9d 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/DevicePolicyManagementRoleBehavior.java
+++ b/PermissionController/src/com/android/role/controller/model/DevicePolicyManagementRoleBehavior.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/DialerRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/DialerRoleBehavior.java
similarity index 63%
rename from PermissionController/src/com/android/permissioncontroller/role/model/DialerRoleBehavior.java
rename to PermissionController/src/com/android/role/controller/model/DialerRoleBehavior.java
index 36be7e8..188a000 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/DialerRoleBehavior.java
+++ b/PermissionController/src/com/android/role/controller/model/DialerRoleBehavior.java
@@ -14,25 +14,19 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
 import android.os.UserHandle;
-import android.telecom.TelecomManager;
 import android.telephony.TelephonyManager;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.preference.Preference;
 
 import com.android.modules.utils.build.SdkLevel;
-import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.role.utils.PackageUtils;
 
 import java.util.Arrays;
 import java.util.List;
-import java.util.Objects;
 
 /**
  * Class for behavior of the dialer role.
@@ -58,33 +52,6 @@
     }
 
     @Override
-    public void prepareApplicationPreferenceAsUser(@NonNull Role role,
-            @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo,
-            @NonNull UserHandle user, @NonNull Context context) {
-        TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
-        String systemPackageName = telecomManager.getSystemDialerPackage();
-        if (Objects.equals(applicationInfo.packageName, systemPackageName)) {
-            preference.setSummary(R.string.default_app_system_default);
-        } else {
-            preference.setSummary(null);
-        }
-    }
-
-    @Nullable
-    @Override
-    public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName,
-            @NonNull Context context) {
-        return EncryptionUnawareConfirmationMixin.getConfirmationMessage(role, packageName,
-                context);
-    }
-
-    @Override
-    public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
-            @NonNull Context context) {
-        return context.getResources().getBoolean(R.bool.config_showDialerRole);
-    }
-
-    @Override
     public void grant(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
         if (SdkLevel.isAtLeastS()) {
             if (PackageUtils.isSystemPackage(packageName, context)) {
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/DocumentManagerRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/DocumentManagerRoleBehavior.java
similarity index 96%
rename from PermissionController/src/com/android/permissioncontroller/role/model/DocumentManagerRoleBehavior.java
rename to PermissionController/src/com/android/role/controller/model/DocumentManagerRoleBehavior.java
index ce307fd..c529c3d 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/DocumentManagerRoleBehavior.java
+++ b/PermissionController/src/com/android/role/controller/model/DocumentManagerRoleBehavior.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Context;
 import android.os.Process;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/EmergencyRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/EmergencyRoleBehavior.java
similarity index 81%
rename from PermissionController/src/com/android/permissioncontroller/role/model/EmergencyRoleBehavior.java
rename to PermissionController/src/com/android/role/controller/model/EmergencyRoleBehavior.java
index d0b2bf4..7efa624 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/EmergencyRoleBehavior.java
+++ b/PermissionController/src/com/android/role/controller/model/EmergencyRoleBehavior.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Context;
 import android.content.pm.PackageInfo;
@@ -66,18 +66,4 @@
         }
         return fallbackPackageInfo != null ? fallbackPackageInfo.packageName : null;
     }
-
-    @Override
-    public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
-            @NonNull Context context) {
-        return VisibilityMixin.isVisible("config_showDefaultEmergency", context);
-    }
-
-    @Nullable
-    @Override
-    public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName,
-            @NonNull Context context) {
-        return EncryptionUnawareConfirmationMixin.getConfirmationMessage(role, packageName,
-                context);
-    }
 }
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/HomeRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/HomeRoleBehavior.java
similarity index 63%
rename from PermissionController/src/com/android/permissioncontroller/role/model/HomeRoleBehavior.java
rename to PermissionController/src/com/android/role/controller/model/HomeRoleBehavior.java
index af6acfe..9a476cd 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/HomeRoleBehavior.java
+++ b/PermissionController/src/com/android/role/controller/model/HomeRoleBehavior.java
@@ -14,30 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
-import android.app.admin.DevicePolicyResources.Strings.DefaultAppSettings;
-import android.app.role.RoleManager;
-import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PermissionInfo;
 import android.content.pm.ResolveInfo;
-import android.os.Build;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.preference.Preference;
 
-import com.android.permissioncontroller.R;
-import com.android.permissioncontroller.permission.utils.CollectionUtils;
-import com.android.permissioncontroller.permission.utils.Utils;
-import com.android.permissioncontroller.role.ui.TwoTargetPreference;
 import com.android.permissioncontroller.role.utils.UserUtils;
 
 import java.util.Arrays;
@@ -53,8 +43,6 @@
  */
 public class HomeRoleBehavior implements RoleBehavior {
 
-    private static final String LOG_TAG = HomeRoleBehavior.class.getSimpleName();
-
     private static final List<String> AUTOMOTIVE_PERMISSIONS = Arrays.asList(
             android.Manifest.permission.READ_CALL_LOG,
             android.Manifest.permission.WRITE_CALL_LOG,
@@ -100,71 +88,10 @@
         return packageName;
     }
 
-    @Override
-    public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
-            @NonNull Context context) {
-        return VisibilityMixin.isVisible("config_showDefaultHome", context);
-    }
-
-    @Override
-    public void preparePreferenceAsUser(@NonNull Role role, @NonNull TwoTargetPreference preference,
-            @NonNull UserHandle user, @NonNull Context context) {
-        TwoTargetPreference.OnSecondTargetClickListener listener = null;
-        RoleManager roleManager = context.getSystemService(RoleManager.class);
-        String packageName = CollectionUtils.firstOrNull(roleManager.getRoleHoldersAsUser(
-                role.getName(), user));
-        if (packageName != null) {
-            Intent intent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
-                    .setPackage(packageName)
-                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-            PackageManager userPackageManager = UserUtils.getUserContext(context, user)
-                    .getPackageManager();
-            ResolveInfo resolveInfo = userPackageManager.resolveActivity(intent, 0);
-            if (resolveInfo != null && resolveInfo.activityInfo != null
-                    && resolveInfo.activityInfo.exported) {
-                listener = preference2 -> {
-                    try {
-                        context.startActivity(intent);
-                    } catch (ActivityNotFoundException e) {
-                        Log.e(LOG_TAG, "Cannot start activity for home app preferences", e);
-                    }
-                };
-            }
-        }
-        preference.setOnSecondTargetClickListener(listener);
-    }
-
-    @Override
-    public boolean isApplicationVisibleAsUser(@NonNull Role role,
-            @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user,
-            @NonNull Context context) {
-        // Home is not available for work profile, so we can just use the current user.
-        return !isSettingsApplication(applicationInfo, context);
-    }
-
-    @Override
-    public void prepareApplicationPreferenceAsUser(@NonNull Role role,
-            @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo,
-            @NonNull UserHandle user, @NonNull Context context) {
-        boolean missingWorkProfileSupport = isMissingWorkProfileSupport(applicationInfo, context);
-        preference.setEnabled(!missingWorkProfileSupport);
-        preference.setSummary(missingWorkProfileSupport ? Utils.getEnterpriseString(context,
-                DefaultAppSettings.HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE,
-                R.string.home_missing_work_profile_support) : null);
-    }
-
-    private boolean isMissingWorkProfileSupport(@NonNull ApplicationInfo applicationInfo,
-            @NonNull Context context) {
-        boolean hasWorkProfile = UserUtils.getWorkProfile(context) != null;
-        if (!hasWorkProfile) {
-            return false;
-        }
-        boolean isWorkProfileSupported = applicationInfo.targetSdkVersion
-                >= Build.VERSION_CODES.LOLLIPOP;
-        return !isWorkProfileSupported;
-    }
-
-    private boolean isSettingsApplication(@NonNull ApplicationInfo applicationInfo,
+    /**
+     * Check if the application is a settings application
+     */
+    public static boolean isSettingsApplication(@NonNull ApplicationInfo applicationInfo,
             @NonNull Context context) {
         PackageManager packageManager = context.getPackageManager();
         ResolveInfo resolveInfo = packageManager.resolveActivity(new Intent(
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/IntentFilterData.java b/PermissionController/src/com/android/role/controller/model/IntentFilterData.java
similarity index 98%
rename from PermissionController/src/com/android/permissioncontroller/role/model/IntentFilterData.java
rename to PermissionController/src/com/android/role/controller/model/IntentFilterData.java
index 0bf1ce5..90deebf 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/IntentFilterData.java
+++ b/PermissionController/src/com/android/role/controller/model/IntentFilterData.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Intent;
 import android.content.IntentFilter;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/Permission.java b/PermissionController/src/com/android/role/controller/model/Permission.java
similarity index 97%
rename from PermissionController/src/com/android/permissioncontroller/role/model/Permission.java
rename to PermissionController/src/com/android/role/controller/model/Permission.java
index 98c023b..0c4a145 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/Permission.java
+++ b/PermissionController/src/com/android/role/controller/model/Permission.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.os.Build;
 
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/PermissionSet.java b/PermissionController/src/com/android/role/controller/model/PermissionSet.java
similarity index 97%
rename from PermissionController/src/com/android/permissioncontroller/role/model/PermissionSet.java
rename to PermissionController/src/com/android/role/controller/model/PermissionSet.java
index 1568c5c..72f127e 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/PermissionSet.java
+++ b/PermissionController/src/com/android/role/controller/model/PermissionSet.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import androidx.annotation.NonNull;
 
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/Permissions.java b/PermissionController/src/com/android/role/controller/model/Permissions.java
similarity index 97%
rename from PermissionController/src/com/android/permissioncontroller/role/model/Permissions.java
rename to PermissionController/src/com/android/role/controller/model/Permissions.java
index 28146a8..75a90b3 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/Permissions.java
+++ b/PermissionController/src/com/android/role/controller/model/Permissions.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.Manifest;
 import android.app.AppOpsManager;
@@ -37,11 +37,11 @@
 
 import com.android.permissioncontroller.permission.utils.ArrayUtils;
 import com.android.permissioncontroller.permission.utils.CollectionUtils;
-import com.android.permissioncontroller.permission.utils.PermissionMapping;
 import com.android.permissioncontroller.permission.utils.Utils;
 import com.android.permissioncontroller.role.utils.PackageUtils;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
 
@@ -57,6 +57,19 @@
     private static ArrayMap<String, String> sForegroundToBackgroundPermission;
     private static ArrayMap<String, List<String>> sBackgroundToForegroundPermissions;
     private static final Object sForegroundBackgroundPermissionMappingsLock = new Object();
+    private static final List<String> SMS_PERMISSIONS = Arrays.asList(
+            Manifest.permission.SEND_SMS,
+            Manifest.permission.RECEIVE_SMS,
+            Manifest.permission.READ_SMS,
+            Manifest.permission.RECEIVE_MMS,
+            Manifest.permission.RECEIVE_WAP_PUSH,
+            Manifest.permission.READ_CELL_BROADCASTS
+    );
+    private static final List<String> CALL_LOG_PERMISSIONS = Arrays.asList(
+            Manifest.permission.READ_CALL_LOG,
+            Manifest.permission.WRITE_CALL_LOG,
+            Manifest.permission.PROCESS_OUTGOING_CALLS
+    );
 
     /**
      * Filter a list of permissions based on their SDK versions.
@@ -181,16 +194,12 @@
         Set<String> whitelistedRestrictedPermissions = new ArraySet<>(
                 packageManager.getWhitelistedRestrictedPermissions(packageName,
                         PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM));
-        List<String> smsPermissions = PermissionMapping.getPlatformPermissionNamesOfGroup(
-                Manifest.permission_group.SMS);
-        List<String> callLogPermissions = PermissionMapping.getPlatformPermissionNamesOfGroup(
-                Manifest.permission_group.CALL_LOG);
 
         int sortedPermissionsToGrantLength = sortedPermissionsToGrant.length;
         for (int i = 0; i < sortedPermissionsToGrantLength; i++) {
             String permission = sortedPermissionsToGrant[i];
 
-            if ((smsPermissions.contains(permission) || callLogPermissions.contains(permission))
+            if ((SMS_PERMISSIONS.contains(permission) || CALL_LOG_PERMISSIONS.contains(permission))
                     && whitelistedRestrictedPermissions.add(permission)) {
                 packageManager.addWhitelistedRestrictedPermission(packageName, permission,
                         PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM);
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/PreferredActivity.java b/PermissionController/src/com/android/role/controller/model/PreferredActivity.java
similarity index 98%
rename from PermissionController/src/com/android/permissioncontroller/role/model/PreferredActivity.java
rename to PermissionController/src/com/android/role/controller/model/PreferredActivity.java
index f03527b..5b9c22b 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/PreferredActivity.java
+++ b/PermissionController/src/com/android/role/controller/model/PreferredActivity.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.ComponentName;
 import android.content.Context;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredActivity.java b/PermissionController/src/com/android/role/controller/model/RequiredActivity.java
similarity index 97%
rename from PermissionController/src/com/android/permissioncontroller/role/model/RequiredActivity.java
rename to PermissionController/src/com/android/role/controller/model/RequiredActivity.java
index def0a6d..6b74dd4 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredActivity.java
+++ b/PermissionController/src/com/android/role/controller/model/RequiredActivity.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.ComponentName;
 import android.content.Context;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredBroadcastReceiver.java b/PermissionController/src/com/android/role/controller/model/RequiredBroadcastReceiver.java
similarity index 97%
rename from PermissionController/src/com/android/permissioncontroller/role/model/RequiredBroadcastReceiver.java
rename to PermissionController/src/com/android/role/controller/model/RequiredBroadcastReceiver.java
index 1f756f6..f191a11 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredBroadcastReceiver.java
+++ b/PermissionController/src/com/android/role/controller/model/RequiredBroadcastReceiver.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.ComponentName;
 import android.content.Context;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredComponent.java b/PermissionController/src/com/android/role/controller/model/RequiredComponent.java
similarity index 99%
rename from PermissionController/src/com/android/permissioncontroller/role/model/RequiredComponent.java
rename to PermissionController/src/com/android/role/controller/model/RequiredComponent.java
index 65c8276..ced185b 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredComponent.java
+++ b/PermissionController/src/com/android/role/controller/model/RequiredComponent.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.ComponentName;
 import android.content.Context;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredContentProvider.java b/PermissionController/src/com/android/role/controller/model/RequiredContentProvider.java
similarity index 97%
rename from PermissionController/src/com/android/permissioncontroller/role/model/RequiredContentProvider.java
rename to PermissionController/src/com/android/role/controller/model/RequiredContentProvider.java
index 76c82fa..c24fa42 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredContentProvider.java
+++ b/PermissionController/src/com/android/role/controller/model/RequiredContentProvider.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.ComponentName;
 import android.content.Context;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredMetaData.java b/PermissionController/src/com/android/role/controller/model/RequiredMetaData.java
similarity index 97%
rename from PermissionController/src/com/android/permissioncontroller/role/model/RequiredMetaData.java
rename to PermissionController/src/com/android/role/controller/model/RequiredMetaData.java
index f7f701f..07b1a29 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredMetaData.java
+++ b/PermissionController/src/com/android/role/controller/model/RequiredMetaData.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.os.Bundle;
 
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredService.java b/PermissionController/src/com/android/role/controller/model/RequiredService.java
similarity index 97%
rename from PermissionController/src/com/android/permissioncontroller/role/model/RequiredService.java
rename to PermissionController/src/com/android/role/controller/model/RequiredService.java
index 72d1439..3df7304 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/RequiredService.java
+++ b/PermissionController/src/com/android/role/controller/model/RequiredService.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.ComponentName;
 import android.content.Context;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/Role.java b/PermissionController/src/com/android/role/controller/model/Role.java
similarity index 89%
rename from PermissionController/src/com/android/permissioncontroller/role/model/Role.java
rename to PermissionController/src/com/android/role/controller/model/Role.java
index ddd7ff6..0c3241d 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/Role.java
+++ b/PermissionController/src/com/android/role/controller/model/Role.java
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.app.ActivityManager;
 import android.app.role.RoleManager;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.SharedLibraryInfo;
@@ -37,13 +36,11 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
-import androidx.preference.Preference;
 
 import com.android.modules.utils.build.SdkLevel;
 import com.android.permissioncontroller.Constants;
 import com.android.permissioncontroller.permission.utils.CollectionUtils;
 import com.android.permissioncontroller.permission.utils.Utils;
-import com.android.permissioncontroller.role.ui.TwoTargetPreference;
 import com.android.permissioncontroller.role.utils.PackageUtils;
 import com.android.permissioncontroller.role.utils.RoleManagerCompat;
 import com.android.permissioncontroller.role.utils.UserUtils;
@@ -223,6 +220,9 @@
     @NonNull
     private final List<PreferredActivity> mPreferredActivities;
 
+    @Nullable
+    private final String mUiBehaviorName;
+
     public Role(@NonNull String name, boolean allowBypassingQualification,
             @Nullable RoleBehavior behavior, @Nullable String defaultHoldersResourceName,
             @StringRes int descriptionResource, boolean exclusive, boolean fallBackToDefaultHolder,
@@ -233,7 +233,8 @@
             boolean showNone, boolean statik, boolean systemOnly, boolean visible,
             @NonNull List<RequiredComponent> requiredComponents,
             @NonNull List<Permission> permissions, @NonNull List<String> appOpPermissions,
-            @NonNull List<AppOp> appOps, @NonNull List<PreferredActivity> preferredActivities) {
+            @NonNull List<AppOp> appOps, @NonNull List<PreferredActivity> preferredActivities,
+            @Nullable String uiBehaviorName) {
         mName = name;
         mAllowBypassingQualification = allowBypassingQualification;
         mBehavior = behavior;
@@ -259,6 +260,7 @@
         mAppOpPermissions = appOpPermissions;
         mAppOps = appOps;
         mPreferredActivities = preferredActivities;
+        mUiBehaviorName = uiBehaviorName;
     }
 
     @NonNull
@@ -352,6 +354,11 @@
         return mPreferredActivities;
     }
 
+    @Nullable
+    public String getUiBehaviorName() {
+        return mUiBehaviorName;
+    }
+
     /**
      * Callback when this role is added to the system for the first time.
      *
@@ -534,110 +541,6 @@
     }
 
     /**
-     * Check whether this role should be visible to user.
-     *
-     * @param user the user to check for
-     * @param context the {@code Context} to retrieve system services
-     *
-     * @return whether this role should be visible to user
-     */
-    public boolean isVisibleAsUser(@NonNull UserHandle user, @NonNull Context context) {
-        return mVisible && (mBehavior == null || mBehavior.isVisibleAsUser(this, user, context));
-    }
-
-    /**
-     * Check whether this role should be visible to user, for current user.
-     *
-     * @param context the {@code Context} to retrieve system services
-     *
-     * @return whether this role should be visible to user.
-     */
-    public boolean isVisible(@NonNull Context context) {
-        return isVisibleAsUser(Process.myUserHandle(), context);
-    }
-
-    /**
-     * Get the {@link Intent} to manage this role, or {@code null} to use the default UI.
-     *
-     * @param user the user to manage this role for
-     * @param context the {@code Context} to retrieve system services
-     *
-     * @return the {@link Intent} to manage this role, or {@code null} to use the default UI.
-     */
-    @Nullable
-    public Intent getManageIntentAsUser(@NonNull UserHandle user, @NonNull Context context) {
-        if (mBehavior != null) {
-            return mBehavior.getManageIntentAsUser(this, user, context);
-        }
-        return null;
-    }
-
-    /**
-     * Prepare a {@link Preference} for this role.
-     *
-     * @param preference the {@link Preference} for this role
-     * @param user the user for this role
-     * @param context the {@code Context} to retrieve system services
-     */
-    public void preparePreferenceAsUser(@NonNull TwoTargetPreference preference,
-            @NonNull UserHandle user, @NonNull Context context) {
-        if (mBehavior != null) {
-            mBehavior.preparePreferenceAsUser(this, preference, user, context);
-        }
-    }
-
-    /**
-     * Check whether a qualifying application should be visible to user.
-     *
-     * @param applicationInfo the {@link ApplicationInfo} for the application
-     * @param user the user for the application
-     * @param context the {@code Context} to retrieve system services
-     *
-     * @return whether the qualifying application should be visible to user
-     */
-    public boolean isApplicationVisibleAsUser(@NonNull ApplicationInfo applicationInfo,
-            @NonNull UserHandle user, @NonNull Context context) {
-        if (mBehavior != null) {
-            return mBehavior.isApplicationVisibleAsUser(this, applicationInfo, user, context);
-        }
-        return true;
-    }
-
-    /**
-     * Prepare a {@link Preference} for an application.
-     *
-     * @param preference the {@link Preference} for the application
-     * @param applicationInfo the {@link ApplicationInfo} for the application
-     * @param user the user for the application
-     * @param context the {@code Context} to retrieve system services
-     */
-    public void prepareApplicationPreferenceAsUser(@NonNull Preference preference,
-            @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user,
-            @NonNull Context context) {
-        if (mBehavior != null) {
-            mBehavior.prepareApplicationPreferenceAsUser(this, preference, applicationInfo, user,
-                    context);
-        }
-    }
-
-    /**
-     * Get the confirmation message for adding an application as a holder of this role.
-     *
-     * @param packageName the package name of the application to get confirmation message for
-     * @param context the {@code Context} to retrieve system services
-     *
-     * @return the confirmation message, or {@code null} if no confirmation is needed
-     */
-    @Nullable
-    public CharSequence getConfirmationMessage(@NonNull String packageName,
-            @NonNull Context context) {
-        if (mBehavior != null) {
-            return mBehavior.getConfirmationMessage(this, packageName, context);
-        }
-        return null;
-    }
-
-    /**
      * Check whether this role is allowed to bypass qualification, if enabled globally.
      *
      * @param context the {@code Context} to retrieve system services
@@ -1085,6 +988,7 @@
                 + ", mAppOpPermissions=" + mAppOpPermissions
                 + ", mAppOps=" + mAppOps
                 + ", mPreferredActivities=" + mPreferredActivities
+                + ", mUiBehaviorName=" + mUiBehaviorName
                 + '}';
     }
 }
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RoleBehavior.java b/PermissionController/src/com/android/role/controller/model/RoleBehavior.java
similarity index 63%
rename from PermissionController/src/com/android/permissioncontroller/role/model/RoleBehavior.java
rename to PermissionController/src/com/android/role/controller/model/RoleBehavior.java
index e5402c2..f0c4fc0 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/RoleBehavior.java
+++ b/PermissionController/src/com/android/role/controller/model/RoleBehavior.java
@@ -14,18 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
 import android.os.UserHandle;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.preference.Preference;
-
-import com.android.permissioncontroller.role.ui.TwoTargetPreference;
 
 import java.util.Collections;
 import java.util.List;
@@ -65,56 +60,6 @@
     }
 
     /**
-     * @see Role#isVisibleAsUser(UserHandle, Context)
-     */
-    default boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
-            @NonNull Context context) {
-        return true;
-    }
-
-    /**
-     * @see Role#getManageIntentAsUser(UserHandle, Context)
-     */
-    @Nullable
-    default Intent getManageIntentAsUser(@NonNull Role role, @NonNull UserHandle user,
-            @NonNull Context context) {
-        return null;
-    }
-
-    /**
-     * @see Role#preparePreferenceAsUser(TwoTargetPreference, UserHandle, Context)
-     */
-    default void preparePreferenceAsUser(@NonNull Role role,
-            @NonNull TwoTargetPreference preference, @NonNull UserHandle user,
-            @NonNull Context context) {}
-
-    /**
-     * @see Role#isApplicationVisibleAsUser(ApplicationInfo, UserHandle, Context)
-     */
-    default boolean isApplicationVisibleAsUser(@NonNull Role role,
-            @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user,
-            @NonNull Context context) {
-        return true;
-    }
-
-    /**
-     * @see Role#prepareApplicationPreferenceAsUser(Preference, ApplicationInfo, UserHandle,
-     *      Context)
-     */
-    default void prepareApplicationPreferenceAsUser(@NonNull Role role,
-            @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo,
-            @NonNull UserHandle user, @NonNull Context context) {}
-
-    /**
-     * @see Role#getConfirmationMessage(String, Context)
-     */
-    @Nullable
-    default CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName,
-            @NonNull Context context) {
-        return null;
-    }
-
-    /**
      * @see Role#shouldAllowBypassingQualification(Context)
      */
     @Nullable
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/RoleParser.java b/PermissionController/src/com/android/role/controller/model/RoleParser.java
similarity index 98%
rename from PermissionController/src/com/android/permissioncontroller/role/model/RoleParser.java
rename to PermissionController/src/com/android/role/controller/model/RoleParser.java
index 3fac0b5..09b12bb 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/RoleParser.java
+++ b/PermissionController/src/com/android/role/controller/model/RoleParser.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -31,8 +31,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.permissioncontroller.R;
-
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
@@ -41,6 +39,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.function.Function;
 
 /**
  * Parser for {@link Role} definitions.
@@ -48,6 +47,11 @@
 @VisibleForTesting
 public class RoleParser {
 
+    /**
+     * Function to retrieve the roles.xml resource from a context
+     */
+    public static volatile Function<Context, XmlResourceParser> sGetRolesXml;
+
     private static final String LOG_TAG = RoleParser.class.getSimpleName();
 
     private static final String TAG_ROLES = "roles";
@@ -92,6 +96,7 @@
     private static final String ATTRIBUTE_SHOW_NONE = "showNone";
     private static final String ATTRIBUTE_STATIC = "static";
     private static final String ATTRIBUTE_SYSTEM_ONLY = "systemOnly";
+    private static final String ATTRIBUTE_UI_BEHAVIOR = "uiBehavior";
     private static final String ATTRIBUTE_VISIBLE = "visible";
     private static final String ATTRIBUTE_MIN_TARGET_SDK_VERSION = "minTargetSdkVersion";
     private static final String ATTRIBUTE_PERMISSION = "permission";
@@ -138,7 +143,7 @@
      */
     @NonNull
     public ArrayMap<String, Role> parse() {
-        try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.roles)) {
+        try (XmlResourceParser parser = sGetRolesXml.apply(mContext)) {
             Pair<ArrayMap<String, PermissionSet>, ArrayMap<String, Role>> xml = parseXml(parser);
             if (xml == null) {
                 return new ArrayMap<>();
@@ -408,6 +413,8 @@
 
         boolean systemOnly = getAttributeBooleanValue(parser, ATTRIBUTE_SYSTEM_ONLY, false);
 
+        String uiBehaviorName = getAttributeValue(parser, ATTRIBUTE_UI_BEHAVIOR);
+
         List<RequiredComponent> requiredComponents = null;
         List<Permission> permissions = null;
         List<String> appOpPermissions = null;
@@ -491,7 +498,7 @@
                 maxSdkVersion, minSdkVersion, overrideUserWhenGranting, requestDescriptionResource,
                 requestTitleResource, requestable, searchKeywordsResource, shortLabelResource,
                 showNone, statik, systemOnly, visible, requiredComponents, permissions,
-                appOpPermissions, appOps, preferredActivities);
+                appOpPermissions, appOps, preferredActivities, uiBehaviorName);
     }
 
     @NonNull
diff --git a/PermissionController/src/com/android/role/controller/model/RoleParserInitializer.java b/PermissionController/src/com/android/role/controller/model/RoleParserInitializer.java
new file mode 100644
index 0000000..81e338d
--- /dev/null
+++ b/PermissionController/src/com/android/role/controller/model/RoleParserInitializer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.role.controller.model;
+
+import com.android.permissioncontroller.R;
+
+/**
+ * Initialize the function to retrieve the roles.xml resource from a context within
+ * PermissionController APK
+ */
+public class RoleParserInitializer {
+
+    /**
+     * Initialize the function to retrieve the roles.xml resource from a context within
+     * PermissionController APK
+     */
+    public static void initialize() {
+        RoleParser.sGetRolesXml = context -> context.getResources().getXml(R.xml.roles);
+    }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/Roles.java b/PermissionController/src/com/android/role/controller/model/Roles.java
similarity index 96%
rename from PermissionController/src/com/android/permissioncontroller/role/model/Roles.java
rename to PermissionController/src/com/android/role/controller/model/Roles.java
index 6312b17..5914ffb 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/Roles.java
+++ b/PermissionController/src/com/android/role/controller/model/Roles.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Context;
 import android.util.ArrayMap;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/SmsRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/SmsRoleBehavior.java
similarity index 84%
rename from PermissionController/src/com/android/permissioncontroller/role/model/SmsRoleBehavior.java
rename to PermissionController/src/com/android/role/controller/model/SmsRoleBehavior.java
index 1515945..b144eab 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/SmsRoleBehavior.java
+++ b/PermissionController/src/com/android/role/controller/model/SmsRoleBehavior.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Context;
 import android.os.Process;
@@ -26,7 +26,6 @@
 import androidx.annotation.Nullable;
 
 import com.android.modules.utils.build.SdkLevel;
-import com.android.permissioncontroller.R;
 import com.android.permissioncontroller.permission.utils.CollectionUtils;
 import com.android.permissioncontroller.role.utils.PackageUtils;
 import com.android.permissioncontroller.role.utils.UserUtils;
@@ -54,9 +53,16 @@
     @Override
     public boolean isAvailableAsUser(@NonNull Role role, @NonNull UserHandle user,
             @NonNull Context context) {
-        if (UserUtils.isProfile(user, context)) {
-            return false;
+        if (SdkLevel.isAtLeastU()) {
+            if (UserUtils.isCloneProfile(user, context)) {
+                return false;
+            }
+        } else {
+            if (UserUtils.isProfile(user, context)) {
+                return false;
+            }
         }
+
         UserManager userManager = context.getSystemService(UserManager.class);
         if (userManager.isRestrictedProfile(user)) {
             return false;
@@ -86,20 +92,6 @@
         return CollectionUtils.firstOrNull(qualifyingPackageNames);
     }
 
-    @Nullable
-    @Override
-    public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName,
-            @NonNull Context context) {
-        return EncryptionUnawareConfirmationMixin.getConfirmationMessage(role, packageName,
-                context);
-    }
-
-    @Override
-    public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
-            @NonNull Context context) {
-        return context.getResources().getBoolean(R.bool.config_showSmsRole);
-    }
-
     @Override
     public void grant(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
         if (SdkLevel.isAtLeastS() && PackageUtils.isSystemPackage(packageName, context)) {
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/SystemShellRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/SystemShellRoleBehavior.java
similarity index 96%
rename from PermissionController/src/com/android/permissioncontroller/role/model/SystemShellRoleBehavior.java
rename to PermissionController/src/com/android/role/controller/model/SystemShellRoleBehavior.java
index 9476e2b..548d301 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/SystemShellRoleBehavior.java
+++ b/PermissionController/src/com/android/role/controller/model/SystemShellRoleBehavior.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/SystemUiRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/SystemUiRoleBehavior.java
similarity index 97%
rename from PermissionController/src/com/android/permissioncontroller/role/model/SystemUiRoleBehavior.java
rename to PermissionController/src/com/android/role/controller/model/SystemUiRoleBehavior.java
index c082329..4d3912a 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/SystemUiRoleBehavior.java
+++ b/PermissionController/src/com/android/role/controller/model/SystemUiRoleBehavior.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/TelevisionRoleBehavior.java b/PermissionController/src/com/android/role/controller/model/TelevisionRoleBehavior.java
similarity index 95%
rename from PermissionController/src/com/android/permissioncontroller/role/model/TelevisionRoleBehavior.java
rename to PermissionController/src/com/android/role/controller/model/TelevisionRoleBehavior.java
index b854e3e..e89b6fe 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/TelevisionRoleBehavior.java
+++ b/PermissionController/src/com/android/role/controller/model/TelevisionRoleBehavior.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/UserDeniedManager.java b/PermissionController/src/com/android/role/controller/model/UserDeniedManager.java
similarity index 98%
rename from PermissionController/src/com/android/permissioncontroller/role/model/UserDeniedManager.java
rename to PermissionController/src/com/android/role/controller/model/UserDeniedManager.java
index afe35f6..cc88f97 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/UserDeniedManager.java
+++ b/PermissionController/src/com/android/role/controller/model/UserDeniedManager.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
 
 import android.content.Context;
 import android.content.SharedPreferences;
diff --git a/PermissionController/tests/inprocess/AndroidTest.xml b/PermissionController/tests/inprocess/AndroidTest.xml
index 403e320..8971a72 100644
--- a/PermissionController/tests/inprocess/AndroidTest.xml
+++ b/PermissionController/tests/inprocess/AndroidTest.xml
@@ -23,7 +23,7 @@
     <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
 
     <!-- Install test -->
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="test-file-name" value="PermissionControllerInProcessTests.apk" />
         <option name="cleanup-apks" value="true" />
     </target_preparer>
diff --git a/PermissionController/tests/mocking/Android.bp b/PermissionController/tests/mocking/Android.bp
index ad7eca8..e6074e3 100644
--- a/PermissionController/tests/mocking/Android.bp
+++ b/PermissionController/tests/mocking/Android.bp
@@ -85,6 +85,7 @@
         "SettingsLibFooterPreference",
         "SettingsLibSelectorWithWidgetPreference",
         "SettingsLibTwoTargetPreference",
+        "SettingsLibActivityEmbedding",
         "androidx.annotation_annotation",
         "permissioncontroller-statsd",
         // The PermissionController build file includes android.car-stubs in its libs dependency
@@ -107,6 +108,7 @@
         "modules-utils-build_system",
         "safety-center-resources-lib",
         "safety-center-internal-data",
+        "safety-label",
         "lottie",
 
         "androidx.test.rules",
diff --git a/PermissionController/tests/mocking/AndroidTest.xml b/PermissionController/tests/mocking/AndroidTest.xml
index 58147c1..2675592 100644
--- a/PermissionController/tests/mocking/AndroidTest.xml
+++ b/PermissionController/tests/mocking/AndroidTest.xml
@@ -23,7 +23,7 @@
     <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
 
     <!-- Install test -->
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="test-file-name" value="PermissionControllerMockingTests.apk" />
         <option name="cleanup-apks" value="true" />
     </target_preparer>
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/RuntimePermissionsUpgradeControllerTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/RuntimePermissionsUpgradeControllerTest.kt
index c09c046..08e9eb6 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/RuntimePermissionsUpgradeControllerTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/service/RuntimePermissionsUpgradeControllerTest.kt
@@ -21,7 +21,11 @@
 import android.Manifest.permission.ACCESS_MEDIA_LOCATION
 import android.Manifest.permission.READ_CALL_LOG
 import android.Manifest.permission.READ_EXTERNAL_STORAGE
+import android.Manifest.permission.READ_MEDIA_AUDIO
+import android.Manifest.permission.READ_MEDIA_IMAGES
+import android.Manifest.permission.READ_MEDIA_VIDEO
 import android.Manifest.permission.SEND_SMS
+import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
 import android.app.ActivityManager
 import android.app.AppOpsManager
 import android.app.job.JobScheduler
@@ -32,21 +36,27 @@
 import android.content.pm.PackageManager
 import android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT
 import android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
+import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET
 import android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
 import android.content.pm.PackageManager.MATCH_FACTORY_ONLY
 import android.content.pm.PermissionInfo
 import android.location.LocationManager
+import android.os.Build
 import android.os.Build.VERSION_CODES.R
 import android.os.UserManager
 import android.permission.PermissionManager
 import android.provider.Settings
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.modules.utils.build.SdkLevel
 import com.android.permissioncontroller.PermissionControllerApplication
 import com.android.permissioncontroller.permission.service.RuntimePermissionsUpgradeController
 import com.android.permissioncontroller.tests.mocking.permission.data.dataRepositories
+import java.util.concurrent.CompletableFuture
 import org.junit.After
+import org.junit.Assume
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -59,11 +69,10 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.timeout
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations.initMocks
 import org.mockito.MockitoSession
 import org.mockito.quality.Strictness.LENIENT
-import java.util.concurrent.CompletableFuture
-import org.mockito.Mockito.`when` as whenever
 
 @RunWith(AndroidJUnit4::class)
 class RuntimePermissionsUpgradeControllerTest {
@@ -85,7 +94,11 @@
     }
 
     /** Latest permission database version known in this test */
-    private val LATEST_VERSION = 9
+    private val LATEST_VERSION = if (SdkLevel.isAtLeastT()) {
+        10
+    } else {
+        9
+    }
 
     /** Use a unique test package name for each test */
     private val TEST_PKG_NAME: String
@@ -133,7 +146,7 @@
                         }
                     }.toIntArray()
                     applicationInfo = ApplicationInfo().apply {
-                        targetSdkVersion = R
+                        targetSdkVersion = pkg.targetSdkVersion
                     }
                 }
             }
@@ -485,6 +498,38 @@
         verifyNotGranted(TEST_PKG_NAME, ACCESS_MEDIA_LOCATION)
     }
 
+    @SdkSuppress(
+        minSdkVersion = Build.VERSION_CODES.TIRAMISU,
+        maxSdkVersion = Build.VERSION_CODES.TIRAMISU,
+        codeName = "Tiramisu"
+    )
+    @Test
+    fun storagePermissionsMigrateToMediaPermissionsWhenVersionIs9() {
+        Assume.assumeTrue(SdkLevel.isAtLeastT() && !SdkLevel.isAtLeastU())
+        whenever(packageManager.isDeviceUpgrading).thenReturn(true)
+        setInitialDatabaseVersion(9)
+        setPackages(
+            Package(TEST_PKG_NAME,
+                Permission(READ_EXTERNAL_STORAGE, isGranted = true,
+                    flags = FLAG_PERMISSION_USER_SET),
+                Permission(WRITE_EXTERNAL_STORAGE, isGranted = true,
+                    flags = FLAG_PERMISSION_USER_SET),
+                Permission(ACCESS_MEDIA_LOCATION, isGranted = true,
+                    flags = FLAG_PERMISSION_USER_SET),
+                Permission(READ_MEDIA_AUDIO, isGranted = false),
+                Permission(READ_MEDIA_VIDEO, isGranted = false),
+                Permission(READ_MEDIA_IMAGES, isGranted = false),
+                targetSdkVersion = 33
+            )
+        )
+
+        upgradeIfNeeded()
+
+        verifyGranted(TEST_PKG_NAME, READ_MEDIA_AUDIO)
+        verifyGranted(TEST_PKG_NAME, READ_MEDIA_VIDEO)
+        verifyGranted(TEST_PKG_NAME, READ_MEDIA_IMAGES)
+    }
+
     @After
     fun resetSystem() {
         // Send low memory notifications for all data repositories which will clear cached data
@@ -502,10 +547,15 @@
     private open class Package(
         val name: String,
         val permissions: List<Permission> = emptyList(),
-        val isPreinstalled: Boolean = false
+        val isPreinstalled: Boolean = false,
+        val targetSdkVersion: Int = R
     ) {
-        constructor(name: String, vararg permission: Permission, isPreinstalled: Boolean = false) :
-                this(name, permission.toList(), isPreinstalled)
+        constructor(
+            name: String,
+            vararg permission: Permission,
+            isPreinstalled: Boolean = false,
+            targetSdkVersion: Int = R
+        ) : this(name, permission.toList(), isPreinstalled, targetSdkVersion)
     }
 
     private class PreinstalledPackage(
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/ReviewPermissionsViewModelTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/ReviewPermissionsViewModelTest.kt
index df6e921..0f42160 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/ReviewPermissionsViewModelTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/ReviewPermissionsViewModelTest.kt
@@ -26,11 +26,11 @@
 import com.android.permissioncontroller.PermissionControllerApplication
 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
 import com.android.permissioncontroller.permission.model.livedatatypes.LightPermission
-import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel
-import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel.PermissionTarget.PERMISSION_BACKGROUND
-import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel.PermissionTarget.PERMISSION_BOTH
-import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel.PermissionTarget.PERMISSION_FOREGROUND
-import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionsViewModel.SummaryMessage
+import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel
+import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel.PermissionTarget.PERMISSION_BACKGROUND
+import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel.PermissionTarget.PERMISSION_BOTH
+import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel.PermissionTarget.PERMISSION_FOREGROUND
+import com.android.permissioncontroller.permission.ui.model.ReviewPermissionsViewModel.SummaryMessage
 import com.android.permissioncontroller.permission.utils.Utils
 import com.android.settingslib.RestrictedLockUtils
 import org.junit.After
@@ -42,10 +42,10 @@
 import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.spy
+import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 import org.mockito.MockitoSession
 import org.mockito.quality.Strictness
-import org.mockito.Mockito.`when` as whenever
 
 /**
  * Unit tests for [ReviewPermissionsViewModel]
@@ -247,4 +247,4 @@
             requestedPermissionsFlags = listOf<Int>().toIntArray()
         }
     }
-}
\ No newline at end of file
+}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/SafetyCenterReceiverTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/SafetyCenterReceiverTest.kt
index 884e683..b4b1abd 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/SafetyCenterReceiverTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/privacysources/SafetyCenterReceiverTest.kt
@@ -41,6 +41,11 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestCoroutineDispatcher
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.test.setMain
 import org.junit.After
 import org.junit.Before
 import org.junit.Ignore
@@ -58,22 +63,6 @@
 import org.mockito.MockitoSession
 import org.mockito.quality.Strictness
 
-private fun runBlockingTest(testBlock: suspend () -> Unit): Unit =
-    error("runBlockingTest unavailable")
-
-private fun Dispatchers.setMain(dispatcher: Any): Unit =
-    error("setMain unavailable")
-
-private fun Dispatchers.resetMain(): Unit =
-    error("resetMain unavailable")
-
-private fun advanceUntilIdle(): Unit =
-    error("advanceUntilIdle unavailable")
-
-typealias TestCoroutineDispatcher = CoroutineDispatcher
-fun TestCoroutineDispatcher.cleanupTestCoroutines(): Unit =
-    error("cleanupTestCoroutines unavailable")
-
 /**
  * Unit tests for [SafetyCenterReceiver]
  */
@@ -89,8 +78,7 @@
         val application = Mockito.mock(PermissionControllerApplication::class.java)
     }
 
-    private val testCoroutineDispatcher: TestCoroutineDispatcher = 
-        error("TestCoroutineDispatcher is unavailable")
+    private val testCoroutineDispatcher = TestCoroutineDispatcher()
 
     @Mock
     lateinit var mockSafetyCenterManager: SafetyCenterManager
@@ -161,7 +149,6 @@
     }
 
     @Test
-    @Ignore("b/239834928")
     fun onReceive_actionSafetyCenterEnabledChanged() = runBlockingTest {
         mockQSTileSettingsFlag()
         safetyCenterReceiver.onReceive(application, Intent(ACTION_SAFETY_CENTER_ENABLED_CHANGED))
@@ -171,7 +158,6 @@
     }
 
     @Test
-    @Ignore("b/239834928")
     fun onReceive_actionSafetyCenterEnabledChanged_safetyCenterDisabled() = runBlockingTest {
         mockQSTileSettingsFlag()
         whenever(mockSafetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
@@ -184,7 +170,6 @@
     }
 
     @Test
-    @Ignore("b/239834928")
     fun onReceive_actionBootCompleted() = runBlockingTest {
         val intent = Intent(ACTION_BOOT_COMPLETED)
 
@@ -198,7 +183,6 @@
     }
 
     @Test
-    @Ignore("b/239834928")
     fun onReceive_actionBootCompleted_safetyCenterDisabled() = runBlockingTest {
         whenever(mockSafetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
         val intent = Intent(ACTION_BOOT_COMPLETED)
@@ -211,7 +195,6 @@
     }
 
     @Test
-    @Ignore("b/239834928")
     fun onReceive_actionRefreshSafetySources() = runBlockingTest {
         val intent = Intent(ACTION_REFRESH_SAFETY_SOURCES)
         intent.putExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS, arrayOf(TEST_PRIVACY_SOURCE_ID))
@@ -225,7 +208,6 @@
     }
 
     @Test
-    @Ignore("b/239834928")
     fun onReceive_actionRefreshSafetySources_noSourcesSpecified() = runBlockingTest {
         val intent = Intent(ACTION_REFRESH_SAFETY_SOURCES)
 
@@ -237,7 +219,6 @@
     }
 
     @Test
-    @Ignore("b/239834928")
     fun onReceive_actionRefreshSafetySources_safetyCenterDisabled() = runBlockingTest {
         whenever(mockSafetyCenterManager.isSafetyCenterEnabled).thenReturn(false)
         val intent = Intent(ACTION_REFRESH_SAFETY_SOURCES)
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/role/model/RoleParserTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/role/model/RoleParserTest.kt
index b8bc2ad..2e7fd53 100644
--- a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/role/model/RoleParserTest.kt
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/role/model/RoleParserTest.kt
@@ -18,16 +18,26 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.platform.app.InstrumentationRegistry
-import com.android.permissioncontroller.role.model.RoleParser
+import com.android.role.controller.model.RoleParser
+import com.android.role.controller.model.RoleParserInitializer
+import org.junit.BeforeClass
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
 class RoleParserTest {
+    companion object {
+        @BeforeClass
+        @JvmStatic
+        fun setupBeforeClass() {
+            RoleParserInitializer.initialize()
+        }
+    }
+
     private val targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext()
 
     @Test
     fun testParseRolesWithValidation() {
         RoleParser(targetContext, true).parse()
     }
-}
\ No newline at end of file
+}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/OWNERS b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/OWNERS
new file mode 100644
index 0000000..5d8b816
--- /dev/null
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 1026964
+
+include /SafetyCenter/OWNERS
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/StatusUiDataTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/StatusUiDataTest.kt
new file mode 100644
index 0000000..96fc5ee
--- /dev/null
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/safetycenter/ui/model/StatusUiDataTest.kt
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permissioncontroller.tests.mocking.safetycenter.ui.model
+
+import android.content.Context
+import android.os.Build
+import android.safetycenter.SafetyCenterData
+import android.safetycenter.SafetyCenterIssue
+import android.safetycenter.SafetyCenterStatus
+import android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING
+import android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_OK
+import android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION
+import android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN
+import android.safetycenter.SafetyCenterStatus.REFRESH_STATUS_DATA_FETCH_IN_PROGRESS
+import android.safetycenter.SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS
+import android.safetycenter.SafetyCenterStatus.REFRESH_STATUS_NONE
+import androidx.test.filters.SdkSuppress
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.safetycenter.ui.model.StatusUiData
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SdkSuppress(maxSdkVersion = Build.VERSION_CODES.TIRAMISU)
+class StatusUiDataTest {
+
+    @Mock private lateinit var mockContext: Context
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+    }
+
+    @Test
+    fun copyForPendingActions_setsCorrectPendingActionsValue() {
+        val copiedWithPendingActions =
+            StatusUiData(STATUS, hasPendingActions = false).copyForPendingActions(true)
+        val copiedWithoutPendingActions =
+            StatusUiData(STATUS, hasPendingActions = true).copyForPendingActions(false)
+
+        assertThat(copiedWithPendingActions.hasPendingActions).isTrue()
+        assertThat(copiedWithoutPendingActions.hasPendingActions).isFalse()
+    }
+
+    @Test
+    fun getTitle_returnsTitle() {
+        assertThat(StatusUiData(STATUS).title).isEqualTo(STATUS.title)
+        assertThat(StatusUiData(ANOTHER_STATUS).title).isEqualTo(ANOTHER_STATUS.title)
+        assertThat(StatusUiData(SafetyCenterData(STATUS, listOf(), listOf(), listOf())).title)
+            .isEqualTo(STATUS.title)
+    }
+
+    @Test
+    fun getOriginalSummary_returnsOriginalSummary() {
+        assertThat(StatusUiData(STATUS).originalSummary).isEqualTo(STATUS.summary)
+        assertThat(StatusUiData(ANOTHER_STATUS).originalSummary).isEqualTo(ANOTHER_STATUS.summary)
+        assertThat(
+                StatusUiData(SafetyCenterData(STATUS, listOf(), listOf(), listOf()))
+                    .originalSummary)
+            .isEqualTo(STATUS.summary)
+    }
+
+    @Test
+    fun getSeverityLevel_returnsSeverityLevel() {
+        assertThat(StatusUiData(STATUS).severityLevel).isEqualTo(STATUS.severityLevel)
+        assertThat(StatusUiData(ANOTHER_STATUS).severityLevel)
+            .isEqualTo(ANOTHER_STATUS.severityLevel)
+        assertThat(
+                StatusUiData(SafetyCenterData(STATUS, listOf(), listOf(), listOf())).severityLevel)
+            .isEqualTo(STATUS.severityLevel)
+    }
+
+    @Test
+    fun getSummary_withoutPendingActions_returnsOriginalSummary() {
+        val dataWithoutPendingActions = StatusUiData(STATUS, hasPendingActions = false)
+
+        val actualSummary = dataWithoutPendingActions.getSummary(mockContext)
+
+        assertThat(actualSummary).isEqualTo(dataWithoutPendingActions.originalSummary)
+    }
+
+    @Test
+    fun getSummary_withPendingActions_returnsQsSummary() {
+        val expectedSummary = "a quick settings summary"
+        whenever(mockContext.getString(R.string.safety_center_qs_status_summary))
+            .thenReturn(expectedSummary)
+
+        val actualSummary = StatusUiData(STATUS, hasPendingActions = true).getSummary(mockContext)
+
+        assertThat(actualSummary).isEqualTo(expectedSummary)
+    }
+
+    @Test
+    fun getContentDescription_returnsContentDescription() {
+        val expectedContentDescription = "a content description"
+        whenever(
+                mockContext.getString(
+                    R.string.safety_status_preference_title_and_summary_content_description,
+                    STATUS.title,
+                    STATUS.summary))
+            .thenReturn(expectedContentDescription)
+
+        val actualContentDescription = StatusUiData(STATUS).getContentDescription(mockContext)
+
+        assertThat(actualContentDescription).isEqualTo(expectedContentDescription)
+    }
+
+    @Test
+    fun fromSafetyCenterData_withIssues_hasIssuesIsTrue() {
+        assertThat(StatusUiData(DATA_WITH_ISSUES).hasIssues).isTrue()
+    }
+
+    @Test
+    fun fromSafetyCenterData_withoutIssues_hasIssuesIsFalse() {
+        assertThat(StatusUiData(DATA_WITHOUT_ISSUES).hasIssues).isFalse()
+    }
+
+    @Test
+    fun hasPendingActions_defaultsFalse() {
+        assertThat(StatusUiData(STATUS).hasPendingActions).isFalse()
+        assertThat(StatusUiData(DATA_WITH_ISSUES).hasPendingActions).isFalse()
+    }
+
+    @Test
+    fun getStatusImageResId_severityOk() {
+        assertThat(uiDataForSeverity(OVERALL_SEVERITY_LEVEL_OK).statusImageResId)
+            .isEqualTo(R.drawable.safety_status_info)
+    }
+
+    @Test
+    fun getStatusImageResId_severityUnknown() {
+        assertThat(uiDataForSeverity(OVERALL_SEVERITY_LEVEL_UNKNOWN).statusImageResId)
+            .isEqualTo(R.drawable.safety_status_info)
+    }
+    @Test
+    fun getStatusImageResId_severityRecommendation() {
+        assertThat(uiDataForSeverity(OVERALL_SEVERITY_LEVEL_RECOMMENDATION).statusImageResId)
+            .isEqualTo(R.drawable.safety_status_recommendation)
+    }
+    @Test
+    fun getStatusImageResId_severityWarning() {
+        assertThat(uiDataForSeverity(OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING).statusImageResId)
+            .isEqualTo(R.drawable.safety_status_warn)
+    }
+
+    @Test
+    fun isRefreshInProgress_dataFetch_isTrue() {
+        assertThat(
+                uiDataForRefreshStatus(REFRESH_STATUS_DATA_FETCH_IN_PROGRESS).isRefreshInProgress)
+            .isTrue()
+    }
+
+    @Test
+    fun isRefreshInProgress_fullRescan_isTrue() {
+        assertThat(
+                uiDataForRefreshStatus(REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS).isRefreshInProgress)
+            .isTrue()
+    }
+
+    @Test
+    fun isRefreshInProgress_none_isFalse() {
+        assertThat(uiDataForRefreshStatus(REFRESH_STATUS_NONE).isRefreshInProgress).isFalse()
+    }
+
+    @Test
+    fun shouldShowRescanButton_severityOk_noIssues_noPendingActions_isTrue() {
+        assertThat(
+                StatusUiData(
+                        statusForSeverity(OVERALL_SEVERITY_LEVEL_OK),
+                        hasIssues = false,
+                        hasPendingActions = false)
+                    .shouldShowRescanButton())
+            .isTrue()
+    }
+
+    @Test
+    fun shouldShowRescanButton_severityUnknown_noIssues_noPendingActions_isTrue() {
+        assertThat(
+                StatusUiData(
+                        statusForSeverity(OVERALL_SEVERITY_LEVEL_UNKNOWN),
+                        hasIssues = false,
+                        hasPendingActions = false)
+                    .shouldShowRescanButton())
+            .isTrue()
+    }
+
+    @Test
+    fun shouldShowRescanButton_hasIssues_isFalse() {
+        assertThat(
+                StatusUiData(
+                        statusForSeverity(OVERALL_SEVERITY_LEVEL_OK),
+                        hasIssues = true,
+                        hasPendingActions = false)
+                    .shouldShowRescanButton())
+            .isFalse()
+    }
+
+    @Test
+    fun shouldShowRescanButton_hasPendingActions_isFalse() {
+        assertThat(
+                StatusUiData(
+                        statusForSeverity(OVERALL_SEVERITY_LEVEL_OK),
+                        hasIssues = false,
+                        hasPendingActions = true)
+                    .shouldShowRescanButton())
+            .isFalse()
+    }
+
+    @Test
+    fun shouldShowRescanButton_severityNotOkOrUnknown_isFalse() {
+        for (severity in
+            listOf(
+                OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING, OVERALL_SEVERITY_LEVEL_RECOMMENDATION)) {
+            assertThat(
+                    StatusUiData(
+                            statusForSeverity(severity),
+                            hasIssues = false,
+                            hasPendingActions = false)
+                        .shouldShowRescanButton())
+                .isFalse()
+        }
+    }
+
+    private companion object {
+        val STATUS =
+            SafetyCenterStatus.Builder("a title", "a summary")
+                .setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
+                .setRefreshStatus(REFRESH_STATUS_NONE)
+                .build()
+
+        val ANOTHER_STATUS =
+            SafetyCenterStatus.Builder("another title", "another summary")
+                .setSeverityLevel(OVERALL_SEVERITY_LEVEL_RECOMMENDATION)
+                .setRefreshStatus(REFRESH_STATUS_DATA_FETCH_IN_PROGRESS)
+                .build()
+
+        val ISSUE = SafetyCenterIssue.Builder("iSsUe_Id", "issue title", "issue summary").build()
+
+        val DATA_WITH_ISSUES = SafetyCenterData(STATUS, listOf(ISSUE), listOf(), listOf())
+        val DATA_WITHOUT_ISSUES = SafetyCenterData(STATUS, listOf(), listOf(), listOf())
+
+        fun statusForSeverity(severityLevel: Int) =
+            SafetyCenterStatus.Builder(STATUS).setSeverityLevel(severityLevel).build()
+
+        fun uiDataForSeverity(severityLevel: Int) = StatusUiData(statusForSeverity(severityLevel))
+
+        fun uiDataForRefreshStatus(refreshStatus: Int) =
+            StatusUiData(SafetyCenterStatus.Builder(STATUS).setRefreshStatus(refreshStatus).build())
+    }
+}
diff --git a/PermissionController/tests/outofprocess/AndroidTest.xml b/PermissionController/tests/outofprocess/AndroidTest.xml
index c47b78e..37b8996 100644
--- a/PermissionController/tests/outofprocess/AndroidTest.xml
+++ b/PermissionController/tests/outofprocess/AndroidTest.xml
@@ -23,7 +23,7 @@
     <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
 
     <!-- Install test -->
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="test-file-name" value="PermissionControllerOutOfProcessTests.apk" />
         <option name="cleanup-apks" value="true" />
     </target_preparer>
diff --git a/PermissionController/tests/permissionui/AndroidTest.xml b/PermissionController/tests/permissionui/AndroidTest.xml
index 946f97e..6e0bf1a 100644
--- a/PermissionController/tests/permissionui/AndroidTest.xml
+++ b/PermissionController/tests/permissionui/AndroidTest.xml
@@ -24,7 +24,7 @@
     <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.permission.apex" />
 
     <!-- Install test -->
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="test-file-name" value="PermissionUiTestCases.apk" />
         <option name="cleanup-apks" value="true" />
     </target_preparer>
diff --git a/SafetyCenter/Config/Android.bp b/SafetyCenter/Config/Android.bp
index 6615dd1..78480bf 100644
--- a/SafetyCenter/Config/Android.bp
+++ b/SafetyCenter/Config/Android.bp
@@ -36,6 +36,7 @@
         "androidx.annotation_annotation",
         "framework-annotations-lib",
         "framework-permission-s",
+        "modules-utils-build",
     ],
     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 8081ae4..f7faf9e 100644
--- a/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java
+++ b/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java
@@ -37,6 +37,8 @@
 
 import androidx.annotation.RequiresApi;
 
+import com.android.modules.utils.build.SdkLevel;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlPullParserFactory;
@@ -58,6 +60,7 @@
     private static final String ATTR_SAFETY_SOURCES_GROUP_TITLE = "title";
     private static final String ATTR_SAFETY_SOURCES_GROUP_SUMMARY = "summary";
     private static final String ATTR_SAFETY_SOURCES_GROUP_STATELESS_ICON_TYPE = "statelessIconType";
+    private static final String ATTR_SAFETY_SOURCES_GROUP_TYPE = "type";
     private static final String ATTR_SAFETY_SOURCE_ID = "id";
     private static final String ATTR_SAFETY_SOURCE_PACKAGE_NAME = "packageName";
     private static final String ATTR_SAFETY_SOURCE_TITLE = "title";
@@ -71,8 +74,14 @@
     private static final String ATTR_SAFETY_SOURCE_LOGGING_ALLOWED = "loggingAllowed";
     private static final String ATTR_SAFETY_SOURCE_REFRESH_ON_PAGE_OPEN_ALLOWED =
             "refreshOnPageOpenAllowed";
+    private static final String ATTR_SAFETY_SOURCE_NOTIFICATIONS_ALLOWED = "notificationsAllowed";
+    private static final String ATTR_SAFETY_SOURCE_DEDUPLICATION_GROUP = "deduplicationGroup";
+    private static final String ATTR_SAFETY_SOURCE_PACKAGE_CERT_HASHES = "packageCertificateHashes";
     private static final String ENUM_STATELESS_ICON_TYPE_NONE = "none";
     private static final String ENUM_STATELESS_ICON_TYPE_PRIVACY = "privacy";
+    private static final String ENUM_GROUP_TYPE_STATEFUL = "stateful";
+    private static final String ENUM_GROUP_TYPE_STATELESS = "stateless";
+    private static final String ENUM_GROUP_TYPE_HIDDEN = "hidden";
     private static final String ENUM_PROFILE_PRIMARY = "primary_profile_only";
     private static final String ENUM_PROFILE_ALL = "all_profiles";
     private static final String ENUM_INITIAL_DISPLAY_STATE_ENABLED = "enabled";
@@ -183,6 +192,18 @@
                                     parser.getAttributeName(i),
                                     resources));
                     break;
+                case ATTR_SAFETY_SOURCES_GROUP_TYPE:
+                    if (SdkLevel.isAtLeastU()) {
+                        builder.setType(
+                                parseGroupType(
+                                        parser.getAttributeValue(i),
+                                        name,
+                                        parser.getAttributeName(i),
+                                        resources));
+                    } else {
+                        throw attributeUnexpected(name, parser.getAttributeName(i));
+                    }
+                    break;
                 default:
                     throw attributeUnexpected(name, parser.getAttributeName(i));
             }
@@ -321,6 +342,46 @@
                                     parser.getAttributeName(i),
                                     resources));
                     break;
+                case ATTR_SAFETY_SOURCE_NOTIFICATIONS_ALLOWED:
+                    if (SdkLevel.isAtLeastU()) {
+                        builder.setNotificationsAllowed(
+                                parseBoolean(
+                                        parser.getAttributeValue(i),
+                                        name,
+                                        parser.getAttributeName(i),
+                                        resources));
+                    } else {
+                        throw attributeUnexpected(name, parser.getAttributeName(i));
+                    }
+                    break;
+                case ATTR_SAFETY_SOURCE_DEDUPLICATION_GROUP:
+                    if (SdkLevel.isAtLeastU()) {
+                        builder.setDeduplicationGroup(
+                                parseStringResourceValue(
+                                        parser.getAttributeValue(i),
+                                        name,
+                                        parser.getAttributeName(i),
+                                        resources));
+                    } else {
+                        throw attributeUnexpected(name, parser.getAttributeName(i));
+                    }
+                    break;
+                case ATTR_SAFETY_SOURCE_PACKAGE_CERT_HASHES:
+                    if (SdkLevel.isAtLeastU()) {
+                        String commaSeparatedHashes =
+                                parseStringResourceValue(
+                                        parser.getAttributeValue(i),
+                                        name,
+                                        parser.getAttributeName(i),
+                                        resources);
+                        String[] splits = commaSeparatedHashes.split(",");
+                        for (int j = 0; j < splits.length; j++) {
+                            builder.addPackageCertificateHash(splits[j]);
+                        }
+                    } else {
+                        throw attributeUnexpected(name, parser.getAttributeName(i));
+                    }
+                    break;
                 default:
                     throw attributeUnexpected(name, parser.getAttributeName(i));
             }
@@ -501,6 +562,25 @@
         }
     }
 
+    private static int parseGroupType(
+            @NonNull String valueString,
+            @NonNull String parent,
+            @NonNull String name,
+            @NonNull Resources resources)
+            throws ParseException {
+        String valueToParse = getValueToParse(valueString, parent, name, resources);
+        switch (valueToParse) {
+            case ENUM_GROUP_TYPE_STATEFUL:
+                return SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL;
+            case ENUM_GROUP_TYPE_STATELESS:
+                return SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS;
+            case ENUM_GROUP_TYPE_HIDDEN:
+                return SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN;
+            default:
+                throw attributeInvalid(valueToParse, parent, name);
+        }
+    }
+
     private static int parseProfile(
             @NonNull String valueString,
             @NonNull String parent,
diff --git a/SafetyCenter/Config/tests/AndroidTest.xml b/SafetyCenter/Config/tests/AndroidTest.xml
index ecb6210..cac418d 100644
--- a/SafetyCenter/Config/tests/AndroidTest.xml
+++ b/SafetyCenter/Config/tests/AndroidTest.xml
@@ -21,7 +21,7 @@
     <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk33ModuleController" />
 
     <!-- Install test -->
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="test-file-name" value="SafetyCenterConfigTests.apk" />
         <option name="cleanup-apks" value="true" />
     </target_preparer>
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 140da54..fe189dc 100644
--- a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt
+++ b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import com.android.modules.utils.build.SdkLevel
 import com.android.safetycenter.config.tests.R
 import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertThrows
@@ -33,7 +34,8 @@
         private val testName: String,
         val configResourceId: Int,
         val errorMessage: String,
-        val causeErrorMessage: String?
+        val causeErrorMessage: String?,
+        val includeCondition: Boolean = true
     ) {
         override fun toString() = testName
     }
@@ -102,6 +104,11 @@
                         "invalid",
                     null),
                 Params(
+                    "ConfigDynamicSafetySourceInvalidId",
+                    R.raw.config_dynamic_safety_source_invalid_id,
+                    "Element dynamic-safety-source invalid",
+                    "Attribute id invalid"),
+                Params(
                     "ConfigDynamicSafetySourceInvalidProfile",
                     R.raw.config_dynamic_safety_source_invalid_profile,
                     "Attribute value \"invalid\" in dynamic-safety-source.profile invalid",
@@ -157,6 +164,11 @@
                     "Element safety-sources-config invalid",
                     "Duplicate id id among safety sources"),
                 Params(
+                    "ConfigIssueOnlySafetySourceInvalidId",
+                    R.raw.config_issue_only_safety_source_invalid_id,
+                    "Element issue-only-safety-source invalid",
+                    "Attribute id invalid"),
+                Params(
                     "ConfigIssueOnlySafetySourceInvalidProfile",
                     R.raw.config_issue_only_safety_source_invalid_profile,
                     "Attribute value \"invalid\" in issue-only-safety-source.profile invalid",
@@ -237,11 +249,30 @@
                     "Element safety-sources-group invalid",
                     "Safety sources group empty"),
                 Params(
+                    "ConfigSafetySourcesGroupHiddenWithOnlyDynamic",
+                    R.raw.config_safety_sources_group_hidden_with_only_dynamic,
+                    "Element safety-sources-group invalid",
+                    "Safety sources groups of type hidden can only contain sources of type " +
+                        "issue-only",
+                    SdkLevel.isAtLeastU()),
+                Params(
+                    "ConfigSafetySourcesGroupHiddenWithOnlyStatic",
+                    R.raw.config_safety_sources_group_hidden_with_only_static,
+                    "Element safety-sources-group invalid",
+                    "Safety sources groups of type hidden can only contain sources of type " +
+                        "issue-only",
+                    SdkLevel.isAtLeastU()),
+                Params(
                     "ConfigSafetySourcesGroupInvalidIcon",
                     R.raw.config_safety_sources_group_invalid_icon,
                     "Attribute value \"invalid\" in safety-sources-group.statelessIconType invalid",
                     null),
                 Params(
+                    "ConfigSafetySourcesGroupInvalidId",
+                    R.raw.config_safety_sources_group_invalid_id,
+                    "Element safety-sources-group invalid",
+                    "Attribute id invalid"),
+                Params(
                     "ConfigSafetySourcesGroupNoId",
                     R.raw.config_safety_sources_group_no_id,
                     "Element safety-sources-group invalid",
@@ -252,11 +283,30 @@
                     "Element safety-sources-group invalid",
                     "Required attribute title missing"),
                 Params(
+                    "ConfigSafetySourcesGroupStatefulWithOnlyIssueOnly",
+                    R.raw.config_safety_sources_group_stateful_with_only_issue_only,
+                    "Element safety-sources-group invalid",
+                    "Safety sources groups containing only sources of type issue-only must be of " +
+                        "type hidden",
+                    SdkLevel.isAtLeastU()),
+                Params(
+                    "ConfigSafetySourcesGroupStatelessWithOnlyIssueOnly",
+                    R.raw.config_safety_sources_group_stateless_with_only_issue_only,
+                    "Element safety-sources-group invalid",
+                    "Safety sources groups containing only sources of type issue-only must be of " +
+                        "type hidden",
+                    SdkLevel.isAtLeastU()),
+                Params(
                     "ConfigStaticSafetySourceDuplicateKey",
                     R.raw.config_static_safety_source_duplicate_key,
                     "Element safety-sources-config invalid",
                     "Duplicate id id among safety sources"),
                 Params(
+                    "ConfigStaticSafetySourceInvalidId",
+                    R.raw.config_static_safety_source_invalid_id,
+                    "Element static-safety-source invalid",
+                    "Attribute id invalid"),
+                Params(
                     "ConfigStaticSafetySourceInvalidProfile",
                     R.raw.config_static_safety_source_invalid_profile,
                     "Attribute value \"invalid\" in static-safety-source.profile invalid",
@@ -282,6 +332,12 @@
                     "Element static-safety-source invalid",
                     "Required attribute title missing"),
                 Params(
+                    "ConfigStaticSafetySourceWithDeduplicationGroups",
+                    R.raw.config_static_safety_source_with_deduplication_groups,
+                    "Element static-safety-source invalid",
+                    "Prohibited attribute deduplicationGroup present",
+                    SdkLevel.isAtLeastU()),
+                Params(
                     "ConfigStaticSafetySourceWithDisplay",
                     R.raw.config_static_safety_source_with_display,
                     "Element static-safety-source invalid",
@@ -292,10 +348,23 @@
                     "Element static-safety-source invalid",
                     "Prohibited attribute loggingAllowed present"),
                 Params(
+                    "ConfigStaticSafetySourceWithNotifications",
+                    R.raw.config_static_safety_source_with_notifications,
+                    "Element static-safety-source invalid",
+                    "Prohibited attribute notificationsAllowed present",
+                    SdkLevel.isAtLeastU()),
+                Params(
                     "ConfigStaticSafetySourceWithPackage",
                     R.raw.config_static_safety_source_with_package,
                     "Element static-safety-source invalid",
-                    "Prohibited attribute packageName present"),
+                    "Prohibited attribute packageName present",
+                    !SdkLevel.isAtLeastU()),
+                Params(
+                    "ConfigStaticSafetySourceWithPackageCertficates",
+                    R.raw.config_static_safety_source_with_package_certs,
+                    "Element static-safety-source invalid",
+                    "Prohibited attribute packageCertificateHashes present",
+                    SdkLevel.isAtLeastU()),
                 Params(
                     "ConfigStaticSafetySourceWithPrimaryAndWork",
                     R.raw.config_static_safety_source_with_primary_and_work,
@@ -347,5 +416,6 @@
                     "Resource name \"@com.android.safetycenter.config.tests:string/missing\" in " +
                         "safety-sources-group.title missing or invalid",
                     null))
+                .filter { it.includeCondition }
     }
 }
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 a8be2cd..8bf445b 100644
--- a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt
+++ b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt
@@ -22,6 +22,7 @@
 import android.safetycenter.config.SafetySourcesGroup
 import androidx.test.core.app.ApplicationProvider.getApplicationContext
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.modules.utils.build.SdkLevel
 import com.android.safetycenter.config.tests.R
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
@@ -68,6 +69,14 @@
                                 .setSearchTermsResId(R.string.reference)
                                 .setLoggingAllowed(false)
                                 .setRefreshOnPageOpenAllowed(true)
+                                .apply {
+                                    if (SdkLevel.isAtLeastU()) {
+                                        setNotificationsAllowed(true)
+                                        setDeduplicationGroup("group")
+                                        addPackageCertificateHash("feed1")
+                                        addPackageCertificateHash("feed2")
+                                    }
+                                }
                                 .build())
                         .addSafetySource(
                             SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
@@ -83,6 +92,14 @@
                                 .setSearchTermsResId(R.string.reference)
                                 .setLoggingAllowed(false)
                                 .setRefreshOnPageOpenAllowed(true)
+                                .apply {
+                                    if (SdkLevel.isAtLeastU()) {
+                                        setNotificationsAllowed(true)
+                                        setDeduplicationGroup("group")
+                                        addPackageCertificateHash("feed1")
+                                        addPackageCertificateHash("feed2")
+                                    }
+                                }
                                 .build())
                         .addSafetySource(
                             SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
@@ -127,6 +144,7 @@
                         .addSafetySource(
                             SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC)
                                 .setId("static_all_optional")
+                                .apply { if (SdkLevel.isAtLeastU()) setPackageName("package") }
                                 .setTitleResId(R.string.reference)
                                 .setTitleForWorkResId(R.string.reference)
                                 .setSummaryResId(R.string.reference)
@@ -152,6 +170,20 @@
                                 .setMaxSeverityLevel(300)
                                 .setLoggingAllowed(false)
                                 .setRefreshOnPageOpenAllowed(true)
+                                .apply {
+                                    if (SdkLevel.isAtLeastU()) {
+                                        setNotificationsAllowed(true)
+                                        setDeduplicationGroup("group")
+                                        addPackageCertificateHash("feed1")
+                                        addPackageCertificateHash("feed2")
+                                    }
+                                }
+                                .build())
+                        .addSafetySource(
+                            SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY)
+                                .setId("id_test_abcxyz_ABCXYZ_012789")
+                                .setPackageName("package")
+                                .setProfile(SafetySource.PROFILE_PRIMARY)
                                 .build())
                         .build())
                 .addSafetySourcesGroup(
@@ -181,6 +213,94 @@
                                 .setProfile(SafetySource.PROFILE_PRIMARY)
                                 .build())
                         .build())
+                .apply {
+                    if (SdkLevel.isAtLeastU()) {
+                        addSafetySourcesGroup(
+                            SafetySourcesGroup.Builder()
+                                .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL)
+                                .setId("stateful_barebone")
+                                .setTitleResId(R.string.reference)
+                                .addSafetySource(
+                                    SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC)
+                                        .setId("stateful_barebone_source")
+                                        .setTitleResId(R.string.reference)
+                                        .setIntentAction("intent")
+                                        .setProfile(SafetySource.PROFILE_PRIMARY)
+                                        .build())
+                                .build())
+                        addSafetySourcesGroup(
+                            SafetySourcesGroup.Builder()
+                                .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL)
+                                .setId("stateful_all_optional")
+                                .setTitleResId(R.string.reference)
+                                .setSummaryResId(R.string.reference)
+                                .setStatelessIconType(
+                                    SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+                                .addSafetySource(
+                                    SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC)
+                                        .setId("stateful_all_optional_source")
+                                        .setTitleResId(R.string.reference)
+                                        .setIntentAction("intent")
+                                        .setProfile(SafetySource.PROFILE_PRIMARY)
+                                        .build())
+                                .build())
+                        addSafetySourcesGroup(
+                            SafetySourcesGroup.Builder()
+                                .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS)
+                                .setId("stateless_barebone")
+                                .setTitleResId(R.string.reference)
+                                .addSafetySource(
+                                    SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC)
+                                        .setId("stateless_barebone_source")
+                                        .setTitleResId(R.string.reference)
+                                        .setIntentAction("intent")
+                                        .setProfile(SafetySource.PROFILE_PRIMARY)
+                                        .build())
+                                .build())
+                        addSafetySourcesGroup(
+                            SafetySourcesGroup.Builder()
+                                .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS)
+                                .setId("stateless_all_optional")
+                                .setTitleResId(R.string.reference)
+                                .setSummaryResId(R.string.reference)
+                                .setStatelessIconType(
+                                    SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+                                .addSafetySource(
+                                    SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC)
+                                        .setId("stateless_all_optional_source")
+                                        .setTitleResId(R.string.reference)
+                                        .setIntentAction("intent")
+                                        .setProfile(SafetySource.PROFILE_PRIMARY)
+                                        .build())
+                                .build())
+                        addSafetySourcesGroup(
+                            SafetySourcesGroup.Builder()
+                                .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN)
+                                .setId("hidden_barebone")
+                                .addSafetySource(
+                                    SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY)
+                                        .setId("hidden_barebone_source")
+                                        .setPackageName("package")
+                                        .setProfile(SafetySource.PROFILE_PRIMARY)
+                                        .build())
+                                .build())
+                        addSafetySourcesGroup(
+                            SafetySourcesGroup.Builder()
+                                .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN)
+                                .setId("hidden_all_optional")
+                                .setTitleResId(R.string.reference)
+                                .setSummaryResId(R.string.reference)
+                                .setStatelessIconType(
+                                    SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+                                .addSafetySource(
+                                    SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY)
+                                        .setId("hidden_all_optional_source")
+                                        .setPackageName("package")
+                                        .setProfile(SafetySource.PROFILE_PRIMARY)
+                                        .build())
+                                .build())
+                    }
+                }
                 .build()
         assertThat(actual).isEqualTo(expected)
     }
diff --git a/SafetyCenter/Config/tests/overlay/Android.bp b/SafetyCenter/Config/tests/overlay/Android.bp
index 06bea53..72b31db 100644
--- a/SafetyCenter/Config/tests/overlay/Android.bp
+++ b/SafetyCenter/Config/tests/overlay/Android.bp
@@ -21,6 +21,7 @@
 android_test_helper_app {
     name: "SafetyCenterConfigTestsOverlay",
     sdk_version: "current",
+    min_sdk_version: "30",
     test_suites: [
         "general-tests",
         "mts-permission",
diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_dynamic.xml b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_dynamic.xml
new file mode 100644
index 0000000..4be2c75
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_dynamic.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+    <safety-sources-config>
+        <safety-sources-group
+            type="hidden"
+            id="id">
+            <dynamic-safety-source
+                id="id"
+                packageName="package"
+                title="@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-v34/config_safety_sources_group_hidden_with_only_static.xml b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_static.xml
new file mode 100644
index 0000000..65f9f0a
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_static.xml
@@ -0,0 +1,14 @@
+<safety-center-config>
+    <safety-sources-config>
+        <safety-sources-group
+            type="hidden"
+            id="id">
+            <static-safety-source
+                id="id"
+                title="@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-v34/config_safety_sources_group_stateful_with_only_issue_only.xml b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_stateful_with_only_issue_only.xml
new file mode 100644
index 0000000..f801ca5
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_stateful_with_only_issue_only.xml
@@ -0,0 +1,13 @@
+<safety-center-config>
+    <safety-sources-config>
+        <safety-sources-group
+            type="stateful"
+            id="id"
+            title="@com.android.safetycenter.config.tests:string/reference">
+            <issue-only-safety-source
+                id="id"
+                packageName="package"
+                profile="primary_profile_only"/>
+        </safety-sources-group>
+    </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_stateless_with_only_issue_only.xml b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_stateless_with_only_issue_only.xml
new file mode 100644
index 0000000..9c05e59
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_stateless_with_only_issue_only.xml
@@ -0,0 +1,13 @@
+<safety-center-config>
+    <safety-sources-config>
+        <safety-sources-group
+            type="stateless"
+            id="id"
+            title="@com.android.safetycenter.config.tests:string/reference">
+            <issue-only-safety-source
+                id="id"
+                packageName="package"
+                profile="primary_profile_only"/>
+        </safety-sources-group>
+    </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_deduplication_groups.xml b/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_deduplication_groups.xml
new file mode 100644
index 0000000..b75969a
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_deduplication_groups.xml
@@ -0,0 +1,32 @@
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+
+<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"
+                summary="@com.android.safetycenter.config.tests:string/reference"
+                intentAction="intent"
+                profile="primary_profile_only"
+                deduplicationGroup="group"/>
+        </safety-sources-group>
+    </safety-sources-config>
+</safety-center-config>
\ No newline at end of file
diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_notifications.xml b/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_notifications.xml
new file mode 100644
index 0000000..920f5c4
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_notifications.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"
+                summary="@com.android.safetycenter.config.tests:string/reference"
+                intentAction="intent"
+                profile="primary_profile_only"
+                notificationsAllowed="true"/>
+        </safety-sources-group>
+    </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_package_certs.xml b/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_package_certs.xml
new file mode 100644
index 0000000..2a99d56
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw-v34/config_static_safety_source_with_package_certs.xml
@@ -0,0 +1,32 @@
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+
+<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"
+                summary="@com.android.safetycenter.config.tests:string/reference"
+                intentAction="intent"
+                profile="primary_profile_only"
+                packageCertificateHashes="feed1,feed2"/>
+        </safety-sources-group>
+    </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_valid.xml b/SafetyCenter/Config/tests/res/raw-v34/config_valid.xml
new file mode 100644
index 0000000..a473ea2
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw-v34/config_valid.xml
@@ -0,0 +1,193 @@
+<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"
+                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"
+                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"
+                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"
+                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_dynamic_safety_source_invalid_id.xml b/SafetyCenter/Config/tests/res/raw/config_dynamic_safety_source_invalid_id.xml
new file mode 100644
index 0000000..9b7d2c7
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw/config_dynamic_safety_source_invalid_id.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">
+            <dynamic-safety-source
+                id="package,id"
+                packageName="package"
+                title="@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/config_issue_only_safety_source_invalid_id.xml b/SafetyCenter/Config/tests/res/raw/config_issue_only_safety_source_invalid_id.xml
new file mode 100644
index 0000000..52f0a28
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw/config_issue_only_safety_source_invalid_id.xml
@@ -0,0 +1,11 @@
+<safety-center-config>
+    <safety-sources-config>
+        <safety-sources-group
+            id="id">
+            <issue-only-safety-source
+                id="package.id"
+                packageName="package"
+                profile="primary_profile_only"/>
+        </safety-sources-group>
+    </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/Config/tests/res/raw/config_safety_sources_group_invalid_id.xml b/SafetyCenter/Config/tests/res/raw/config_safety_sources_group_invalid_id.xml
new file mode 100644
index 0000000..4be7533
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw/config_safety_sources_group_invalid_id.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+    <safety-sources-config>
+        <safety-sources-group
+            id="something: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"
+                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/config_static_safety_source_invalid_id.xml b/SafetyCenter/Config/tests/res/raw/config_static_safety_source_invalid_id.xml
new file mode 100644
index 0000000..3929645
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw/config_static_safety_source_invalid_id.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">
+            <static-safety-source
+                id="something-id"
+                title="@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/config_valid.xml b/SafetyCenter/Config/tests/res/raw/config_valid.xml
index 8a3ee09..50b9c99 100644
--- a/SafetyCenter/Config/tests/res/raw/config_valid.xml
+++ b/SafetyCenter/Config/tests/res/raw/config_valid.xml
@@ -91,6 +91,10 @@
                 maxSeverityLevel="300"
                 loggingAllowed="false"
                 refreshOnPageOpenAllowed="true"/>
+            <issue-only-safety-source
+                id="id_test_abcxyz_ABCXYZ_012789"
+                packageName="package"
+                profile="primary_profile_only"/>
         </safety-sources-group>
         <safety-sources-group
             id="mixed"
diff --git a/SafetyCenter/Config/tests/res/values/strings.xml b/SafetyCenter/Config/tests/res/values/strings.xml
index 195f56c..a625c52 100644
--- a/SafetyCenter/Config/tests/res/values/strings.xml
+++ b/SafetyCenter/Config/tests/res/values/strings.xml
@@ -26,4 +26,7 @@
     <string name="dynamic_all_references_max_severity_level" translatable="false">300</string>
     <string name="dynamic_all_references_logging_allowed" translatable="false">false</string>
     <string name="dynamic_all_references_refresh_on_page_open_allowed" translatable="false">true</string>
+    <string name="dynamic_all_references_notifications_allowed" translatable="false">true</string>
+    <string name="dynamic_all_references_deduplication_group" translatable="false">group</string>
+    <string name="dynamic_all_references_package_cert_hashes" translatable="false">feed1,feed2</string>
 </resources>
diff --git a/SafetyCenter/ConfigLintChecker/Android.bp b/SafetyCenter/ConfigLintChecker/Android.bp
index 4a8db0f..08881cc 100644
--- a/SafetyCenter/ConfigLintChecker/Android.bp
+++ b/SafetyCenter/ConfigLintChecker/Android.bp
@@ -33,7 +33,7 @@
         "layoutlib_api-prebuilt", // For com.android.resources.ResourceFolderType
         "lint_api",
     ],
-    java_resources: [":safetycenter-config-schema"],
+    java_resources: [":safetycenter-config-schemas"],
     jarjar_rules: "jarjar-rules.txt",
     kotlincflags: ["-Xjvm-default=all"],
     visibility: [
diff --git a/SafetyCenter/ConfigLintChecker/java/android/annotation/SuppressLint.java b/SafetyCenter/ConfigLintChecker/java/android/annotation/SuppressLint.java
new file mode 100644
index 0000000..c6d6dc7
--- /dev/null
+++ b/SafetyCenter/ConfigLintChecker/java/android/annotation/SuppressLint.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Indicates that Lint should ignore the specified warnings for the annotated element. */
+@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
+@Retention(RetentionPolicy.CLASS)
+public @interface SuppressLint {
+    /**
+     * The set of warnings (identified by the lint issue id) that should be ignored by lint. It is
+     * not an error to specify an unrecognized name.
+     */
+    String[] value();
+}
diff --git a/SafetyCenter/ConfigLintChecker/java/android/os/Build.java b/SafetyCenter/ConfigLintChecker/java/android/os/Build.java
index abe5d49..531b6e4 100644
--- a/SafetyCenter/ConfigLintChecker/java/android/os/Build.java
+++ b/SafetyCenter/ConfigLintChecker/java/android/os/Build.java
@@ -20,9 +20,11 @@
 public final class Build {
     private Build() {}
 
-    /** Stub class to used in the Safety Center config files. */
+    /** Stub class used in the Safety Center config code. */
     public static final class VERSION_CODES {
-        /** Constant used in the Safety Center config files. */
-        public static final int TIRAMISU = 10000;
+        /** Constant used in the Safety Center config code. */
+        public static final int TIRAMISU = 33;
+        /** Constant used in the Safety Center config code. */
+        public static final int UPSIDE_DOWN_CAKE = 34;
     }
 }
diff --git a/SafetyCenter/ConfigLintChecker/java/android/os/Parcel.java b/SafetyCenter/ConfigLintChecker/java/android/os/Parcel.java
index e1300de..81bedc8 100644
--- a/SafetyCenter/ConfigLintChecker/java/android/os/Parcel.java
+++ b/SafetyCenter/ConfigLintChecker/java/android/os/Parcel.java
@@ -29,6 +29,8 @@
     int readInt();
     /** Method used in the Safety Center config data structures. */
     String readString();
+    /** Method used in the Safety Center config data structures. */
+    ArrayList<String> createStringArrayList();
 
     /** Method used in the Safety Center config data structures. */
     void writeBoolean(boolean value);
@@ -37,5 +39,7 @@
     /** Method used in the Safety Center config data structures. */
     void writeString(String value);
     /** Method used in the Safety Center config data structures. */
+    void writeStringList(List<String> value);
+    /** Method used in the Safety Center config data structures. */
     <T extends Parcelable> void writeTypedList(List<T> value);
 }
diff --git a/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ConfigSchemaDetector.kt b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ConfigSchemaDetector.kt
index ed58e53..0b00805 100644
--- a/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ConfigSchemaDetector.kt
+++ b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ConfigSchemaDetector.kt
@@ -16,6 +16,7 @@
 
 package android.safetycenter.lint
 
+import android.os.Build.VERSION_CODES.TIRAMISU
 import com.android.resources.ResourceFolderType
 import com.android.tools.lint.detector.api.Category
 import com.android.tools.lint.detector.api.Context
@@ -36,20 +37,19 @@
 class ConfigSchemaDetector : Detector(), OtherFileScanner {
 
     companion object {
-        val ISSUE = Issue.create(
-            id = "InvalidSafetyCenterConfigSchema",
-            briefDescription = "The Safety Center config does not meet the schema requirements",
-            explanation = """The Safety Center config must follow all constraints defined in \
+        val ISSUE =
+            Issue.create(
+                id = "InvalidSafetyCenterConfigSchema",
+                briefDescription = "The Safety Center config does not meet the schema requirements",
+                explanation =
+                    """The Safety Center config must follow all constraints defined in \
                 safety_center_config.xsd. Either the config is invalid or the schema is not up to
                 date.""",
-            category = Category.CORRECTNESS,
-            severity = Severity.ERROR,
-            implementation = Implementation(
-                ConfigSchemaDetector::class.java,
-                Scope.OTHER_SCOPE
-            ),
-            androidSpecific = true
-        )
+                category = Category.CORRECTNESS,
+                severity = Severity.ERROR,
+                implementation =
+                    Implementation(ConfigSchemaDetector::class.java, Scope.OTHER_SCOPE),
+                androidSpecific = true)
     }
 
     override fun appliesTo(folderType: ResourceFolderType): Boolean {
@@ -60,9 +60,31 @@
         if (context.file.name != "safety_center_config.xml") {
             return
         }
-        val xsd = StreamSource(
-            ConfigSchemaDetector::class.java.getResourceAsStream("/safety_center_config.xsd")
-        )
+        val fileSdk = FileSdk.getSdkQualifier(context.file)
+        // A config must comply with the schema at the highest SDK level that is lower or equal to
+        // the SDK level of the config itself.
+        var found = false
+        for (sdk in fileSdk downTo TIRAMISU) {
+            if (testSchema(sdk, context)) {
+                found = true
+                break
+            }
+        }
+        if (!found) {
+            context.report(
+                ISSUE,
+                Location.create(context.file),
+                "No schema found for SDK level: $fileSdk, was it deleted?")
+        }
+        // Test new schemas for backward compatibility.
+        for (sdk in fileSdk + 1..FileSdk.getMaxSdkVersion()) {
+            testSchema(sdk, context)
+        }
+    }
+
+    private fun testSchema(sdk: Int, context: Context): Boolean {
+        val xsdInputStream = FileSdk.getSchemaAsStream(sdk) ?: return false
+        val xsd = StreamSource(xsdInputStream)
         val xml = StreamSource(context.file.inputStream())
         val schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
         try {
@@ -73,14 +95,13 @@
             context.report(
                 ISSUE,
                 Location.create(context.file),
-                e.message!!
-            )
+                "SAXException exception at sdk=$sdk: \"${e.message}\"")
         } catch (e: IOException) {
             context.report(
                 ISSUE,
                 Location.create(context.file),
-                e.message!!
-            )
+                "IOException exception at sdk=$sdk: \"${e.message}\"")
         }
+        return true
     }
-}
\ No newline at end of file
+}
diff --git a/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/FileSdk.kt b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/FileSdk.kt
new file mode 100644
index 0000000..f93d4ec
--- /dev/null
+++ b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/FileSdk.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.safetycenter.lint
+
+import android.os.Build
+import android.os.Build.VERSION_CODES.TIRAMISU
+import com.google.common.annotations.VisibleForTesting
+import java.io.File
+import java.io.InputStream
+import java.lang.reflect.Modifier.isFinal
+import java.lang.reflect.Modifier.isPublic
+import java.lang.reflect.Modifier.isStatic
+
+/** A class that allows interacting with files that are versioned by sdk. */
+object FileSdk {
+    /**
+     * Linter constant to limit the mocked SDK levels that will be checked. We are making an
+     * important assumption here that if new parser logic is introduced that depends on a new SDK
+     * level, we expect a new schema to exist and a new version code to have been added.
+     */
+    private val MAX_VERSION: Int = maxOf(getMaxVersionCodesConstant(), getMaxSchemaVersion())
+
+    /** Test only override to further limit the mocked SDK that will be checked in a test */
+    @VisibleForTesting @Volatile @JvmStatic var maxVersionOverride: Int? = null
+
+    /**
+     * Returns the max SDK level version that should be used while linting to check for backward
+     * compatibility.
+     */
+    fun getMaxSdkVersion(): Int = maxVersionOverride ?: MAX_VERSION
+
+    /** Returns the SDK level version that a file resource belongs to. */
+    fun getSdkQualifier(file: File): Int {
+        val directParentName = file.parentFile.name
+        val lastQualifier = directParentName.substringAfterLast("-", "")
+        if (lastQualifier.isEmpty() || lastQualifier[0] != 'v') {
+            return TIRAMISU
+        }
+        return try {
+            lastQualifier.substring(1).toInt()
+        } catch (nfe: NumberFormatException) {
+            TIRAMISU
+        }
+    }
+
+    /** Returns the schema for the specific SDK level provided or null if it doesn't exist. */
+    fun getSchemaAsStream(sdk: Int): InputStream? =
+        FileSdk::class.java.getResourceAsStream("/safety_center_config${toQualifier(sdk)}.xsd")
+
+    private fun toQualifier(sdk: Int): String = if (sdk == TIRAMISU) "" else "-v$sdk"
+
+    private fun getMaxVersionCodesConstant(): Int =
+        Build.VERSION_CODES::class
+            .java
+            .declaredFields
+            .filter {
+                isPublic(it.modifiers) &&
+                    isFinal(it.modifiers) &&
+                    isStatic(it.modifiers) &&
+                    it.type == Integer.TYPE
+            }
+            .maxOf { it.get(null) as Int }
+
+    private fun getMaxSchemaVersion(): Int =
+        // 99 is an arbitrary high value to look for the schema with the highest SDK level.
+        // Gaps are possible which is why we cannot just stop as soon as an SDK level has no schema.
+        (TIRAMISU..99).filter { getSchemaAsStream(it) != null }.maxOrNull() ?: 0
+}
diff --git a/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt
index e512b7e..09f0748 100644
--- a/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt
+++ b/SafetyCenter/ConfigLintChecker/java/android/safetycenter/lint/ParserExceptionDetector.kt
@@ -19,6 +19,7 @@
 import android.content.res.Resources
 import com.android.SdkConstants.ATTR_NAME
 import com.android.SdkConstants.TAG_STRING
+import com.android.modules.utils.build.SdkLevel
 import com.android.resources.ResourceFolderType
 import com.android.safetycenter.config.ParseException
 import com.android.safetycenter.config.SafetyCenterConfigParser
@@ -103,21 +104,34 @@
             context.file.name != "safety_center_config.xml") {
             return
         }
-        try {
-            SafetyCenterConfigParser.parseXmlResource(
-                context.file.inputStream(),
-                // Note: using a map of the string resources present in the APK under analysis is
-                // necessary in order to get the value of string resources that are resolved and
-                // validated at parse time. The drawback of this is that the linter cannot be used
-                // on overlay packages that refer to resources in the target package or on packages
-                // that refer to Android global resources. However, we cannot use custom a linter
-                // with the default soong overlay build rule regardless.
-                Resources(context.project.`package`, mNameToIndex, mIndexToValue))
-        } catch (e: ParseException) {
-            context.report(
-                ISSUE,
-                Location.create(context.file),
-                "Parser exception: \"${e.message}\", cause: \"${e.cause?.message}\"")
+        val minSdk = FileSdk.getSdkQualifier(context.file)
+        val maxSdk = maxOf(minSdk, FileSdk.getMaxSdkVersion())
+        // Test the parser at the SDK level for which the config was designed.
+        // Then test parsers at higher SDK levels for backward compatibility.
+        // This is slightly inefficient if a parser at a higher SDK level has no behavioral changes
+        // compared to one at a lower SDK level, but doing an exhaustive search is safer.
+        for (sdk in minSdk..maxSdk) {
+            synchronized(SdkLevel::class.java) {
+                SdkLevel.setSdkInt(sdk)
+                try {
+                    SafetyCenterConfigParser.parseXmlResource(
+                        context.file.inputStream(),
+                        // Note: using a map of the string resources present in the APK under
+                        // analysis is necessary in order to get the value of string resources that
+                        // are resolved and validated at parse time. The drawback of this is that
+                        // the linter cannot be used on overlay packages that refer to resources in
+                        // the target package or on packages that refer to Android global resources.
+                        // However, we cannot use a custom linter with the default soong overlay
+                        // build rule regardless.
+                        Resources(context.project.`package`, mNameToIndex, mIndexToValue))
+                } catch (e: ParseException) {
+                    context.report(
+                        ISSUE,
+                        Location.create(context.file),
+                        "Parser exception at sdk=$sdk: \"${e.message}\", cause: " +
+                            "\"${e.cause?.message}\"")
+                }
+            }
         }
     }
 }
diff --git a/service/java/com/android/permission/access/package-info.java b/SafetyCenter/ConfigLintChecker/java/android/util/ArraySet.java
similarity index 61%
copy from service/java/com/android/permission/access/package-info.java
copy to SafetyCenter/ConfigLintChecker/java/android/util/ArraySet.java
index af115be..cfc864c 100644
--- a/service/java/com/android/permission/access/package-info.java
+++ b/SafetyCenter/ConfigLintChecker/java/android/util/ArraySet.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -14,9 +14,15 @@
  * limitations under the License.
  */
 
+package android.util;
+
+import java.util.HashSet;
+
 /**
- * @hide
- * TODO(b/146466118) remove this javadoc tag
+ * A simple ArraySet implementation for the lint checker.
+ *
+ * <p>It's not array based, but for this simple purpose that doesn't matter.
+ *
+ * @param <E> the type of elements maintained by this set
  */
-@android.annotation.Hide
-package com.android.permission.access;
+public final class ArraySet<E> extends HashSet<E> {}
diff --git a/SafetyCenter/ConfigLintChecker/java/com/android/modules/utils/build/SdkLevel.java b/SafetyCenter/ConfigLintChecker/java/com/android/modules/utils/build/SdkLevel.java
new file mode 100644
index 0000000..dbfaa56
--- /dev/null
+++ b/SafetyCenter/ConfigLintChecker/java/com/android/modules/utils/build/SdkLevel.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.modules.utils.build;
+
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+
+/** Stub class to compile the linter for host execution. */
+public final class SdkLevel {
+    private SdkLevel() {}
+
+    private static volatile int sSdkInt = TIRAMISU;
+
+    /**
+     * Linter only method to set the mocked SDK level for the Safety Center config code.
+     *
+     * <p>You must hold the class lock before calling this method. You should hold the class lock
+     * for the whole duration of the lint check.
+     */
+    public static void setSdkInt(int sdkInt) {
+        if (!Thread.holdsLock(SdkLevel.class)) {
+            throw new IllegalStateException("Lock not held.");
+        }
+        sSdkInt = sdkInt;
+    }
+
+    /** Method used in the Safety Center config code. */
+    public static boolean isAtLeastU() {
+        return sSdkInt >= UPSIDE_DOWN_CAKE;
+    }
+}
diff --git a/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ConfigSchemaDetectorTest.kt b/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ConfigSchemaDetectorTest.kt
index 0dd4afb..754d1a7 100644
--- a/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ConfigSchemaDetectorTest.kt
+++ b/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ConfigSchemaDetectorTest.kt
@@ -16,11 +16,14 @@
 
 package android.safetycenter.lint.test
 
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
 import android.safetycenter.lint.ConfigSchemaDetector
+import android.safetycenter.lint.FileSdk
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
 import com.android.tools.lint.checks.infrastructure.TestLintTask
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Issue
+import org.junit.After
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
@@ -34,11 +37,18 @@
 
     override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
 
+    @After
+    fun resetOverrides() {
+        FileSdk.maxVersionOverride = null
+    }
+
     @Test
-    fun validConfig_doesNotThrow() {
-        lint().files((
-            xml("res/raw/safety_center_config.xml",
-                """
+    fun validMinimumConfig_doesNotThrow() {
+        lint()
+            .files(
+                (xml(
+                    "res/raw/safety_center_config.xml",
+                    """
 <safety-center-config>
     <safety-sources-config>
         <safety-sources-group
@@ -54,26 +64,109 @@
         </safety-sources-group>
     </safety-sources-config>
 </safety-center-config>
-                """))).run().expectClean()
+                    """)))
+            .run()
+            .expectClean()
     }
 
     @Test
-    fun invalidConfig_throws() {
-        lint().files((xml("res/raw/safety_center_config.xml", "<invalid-root/>")))
-            .run().expect("res/raw/safety_center_config.xml: Error: cvc-elt.1.a: Cannot find the " +
-                "declaration of element 'invalid-root'. [InvalidSafetyCenterConfigSchema]\n1 " +
-                "errors, 0 warnings")
+    fun validFutureConfig_doesNotThrow() {
+        lint()
+            .files(
+                (xml(
+                    "res/raw-99/safety_center_config.xml",
+                    """
+<safety-center-config>
+    <safety-sources-config>
+        <safety-sources-group
+            id="group"
+            title="@package:string/reference"
+            summary="@package:string/reference">
+            <static-safety-source
+                id="source"
+                title="@package:string/reference"
+                summary="@package:string/reference"
+                intentAction="intent"
+                profile="primary_profile_only"/>
+        </safety-sources-group>
+    </safety-sources-config>
+</safety-center-config>
+                    """)))
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun invalidConfigWithSingleSchema_throwsOneError() {
+        FileSdk.maxVersionOverride = UPSIDE_DOWN_CAKE
+        lint()
+            .files((xml("res/raw-v34/safety_center_config.xml", "<invalid-root/>")))
+            .run()
+            .expect(
+                "res/raw-v34/safety_center_config.xml: Error: SAXException exception at sdk=34: " +
+                    "\"cvc-elt.1.a: Cannot find the declaration of element 'invalid-root'.\" " +
+                    "[InvalidSafetyCenterConfigSchema]\n1 errors, 0 warnings")
+    }
+
+    @Test
+    fun invalidConfigWithMultipleSchemas_throwsMultipleErrors() {
+        FileSdk.maxVersionOverride = UPSIDE_DOWN_CAKE
+        lint()
+            .files((xml("res/raw/safety_center_config.xml", "<invalid-root/>")))
+            .run()
+            .expect(
+                "res/raw/safety_center_config.xml: Error: SAXException exception at sdk=33: " +
+                    "\"cvc-elt.1.a: Cannot find the declaration of element 'invalid-root'.\" " +
+                    "[InvalidSafetyCenterConfigSchema]\nres/raw/safety_center_config.xml: " +
+                    "Error: SAXException exception at sdk=34: \"cvc-elt.1.a: Cannot find the " +
+                    "declaration of element 'invalid-root'.\" " +
+                    "[InvalidSafetyCenterConfigSchema]\n2 errors, 0 warnings")
+    }
+
+    @Test
+    fun validUConfigWithUFields_throwsInT() {
+        FileSdk.maxVersionOverride = UPSIDE_DOWN_CAKE
+        lint()
+            .files(
+                (xml(
+                    "res/raw/safety_center_config.xml",
+                    """
+<safety-center-config>
+    <safety-sources-config>
+        <safety-sources-group
+            id="group"
+            title="@package:string/reference"
+            summary="@package:string/reference">
+            <dynamic-safety-source
+                id="source"
+                packageName="package"
+                title="@package:string/reference"
+                summary="@package:string/reference"
+                intentAction="intent"
+                profile="primary_profile_only"
+                notificationsAllowed="true"/>
+        </safety-sources-group>
+    </safety-sources-config>
+</safety-center-config>
+                    """)))
+            .run()
+            .expect(
+                "res/raw/safety_center_config.xml: Error: SAXException exception at sdk=33: " +
+                    "\"cvc-complex-type.3.2.2: Attribute 'notificationsAllowed' is not allowed " +
+                    "to appear in element 'dynamic-safety-source'.\" " +
+                    "[InvalidSafetyCenterConfigSchema]\n1 errors, 0 warnings")
     }
 
     @Test
     fun unrelatedFile_doesNotThrow() {
-        lint().files((xml("res/raw/some_other_config.xml", "<some-other-root/>")))
-            .run().expectClean()
+        lint()
+            .files((xml("res/raw/some_other_config.xml", "<some-other-root/>")))
+            .run()
+            .expectClean()
     }
 
     @Test
     fun unrelatedFolder_doesNotThrow() {
-        lint().files((xml("res/values/strings.xml", "<some-other-root/>")))
-            .run().expectClean()
+        lint().files((xml("res/values/strings.xml", "<some-other-root/>"))).run().expectClean()
     }
 }
diff --git a/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt b/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt
index ad7d366..a303055 100644
--- a/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt
+++ b/SafetyCenter/ConfigLintChecker/tests/java/android/safetycenter/lint/test/ParserExceptionDetectorTest.kt
@@ -16,11 +16,15 @@
 
 package android.safetycenter.lint.test
 
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+import android.safetycenter.lint.FileSdk
 import android.safetycenter.lint.ParserExceptionDetector
 import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
 import com.android.tools.lint.checks.infrastructure.TestLintTask
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Issue
+import org.junit.After
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
@@ -34,13 +38,94 @@
 
     override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
 
+    @After
+    fun resetOverrides() {
+        FileSdk.maxVersionOverride = null
+    }
+
     @Test
-    fun validConfig_doesNotThrow() {
+    fun validMinimumConfig_doesNotThrow() {
         lint()
             .files(
-                (xml(
-                    "res/raw/safety_center_config.xml",
-                    """
+                xml("res/raw/safety_center_config.xml", VALID_TIRAMISU_CONFIG),
+                STRINGS_WITH_REFERENCE_XML)
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun validFutureConfig_doesNotThrow() {
+        lint()
+            .files(
+                xml("res/raw-99/safety_center_config.xml", VALID_TIRAMISU_CONFIG),
+                STRINGS_WITH_REFERENCE_XML)
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun invalidConfigWithSingleSchema_throwsOneError() {
+        FileSdk.maxVersionOverride = UPSIDE_DOWN_CAKE
+        lint()
+            .files(xml("res/raw-v34/safety_center_config.xml", "<invalid-root/>"))
+            .run()
+            .expect(
+                "res/raw-v34/safety_center_config.xml: Error: Parser exception at sdk=34: " +
+                    "\"Element safety-center-config missing\", cause: \"null\" " +
+                    "[InvalidSafetyCenterConfig]\n1 errors, 0 warnings")
+    }
+
+    @Test
+    fun invalidConfigWithMultipleSchemas_throwsMultipleErrors() {
+        FileSdk.maxVersionOverride = UPSIDE_DOWN_CAKE
+        lint()
+            .files(xml("res/raw/safety_center_config.xml", "<invalid-root/>"))
+            .run()
+            .expect(
+                "res/raw/safety_center_config.xml: Error: Parser exception at sdk=33: " +
+                    "\"Element safety-center-config missing\", cause: \"null\" " +
+                    "[InvalidSafetyCenterConfig]\nres/raw/safety_center_config.xml: " +
+                    "Error: Parser exception at sdk=34: " +
+                    "\"Element safety-center-config missing\", cause: \"null\" " +
+                    "[InvalidSafetyCenterConfig]\n2 errors, 0 warnings")
+    }
+
+    @Test
+    fun validUConfigWithUFields_throwsInT() {
+        FileSdk.maxVersionOverride = UPSIDE_DOWN_CAKE
+        lint()
+            .files(
+                xml("res/raw/safety_center_config.xml", VALID_UDC_CONFIG),
+                STRINGS_WITH_REFERENCE_XML)
+            .run()
+            .expect(
+                "res/raw/safety_center_config.xml: Error: Parser exception at sdk=33: " +
+                    "\"Unexpected attribute dynamic-safety-source.notificationsAllowed\", cause: " +
+                    "\"null\" [InvalidSafetyCenterConfig]\n1 errors, 0 warnings")
+    }
+
+    @Test
+    fun unrelatedFile_doesNotThrow() {
+        lint().files(xml("res/raw/some_other_config.xml", "<some-other-root/>")).run().expectClean()
+    }
+
+    @Test
+    fun unrelatedFolder_doesNotThrow() {
+        lint().files(xml("res/values/strings.xml", "<some-other-root/>")).run().expectClean()
+    }
+
+    private companion object {
+        val STRINGS_WITH_REFERENCE_XML: TestFile =
+            xml(
+                "res/values/strings.xml",
+                """
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="reference" translatable="false">Reference</string>
+</resources>
+                """)
+
+        const val VALID_TIRAMISU_CONFIG =
+            """
 <safety-center-config>
     <safety-sources-config>
         <safety-sources-group
@@ -56,39 +141,27 @@
         </safety-sources-group>
     </safety-sources-config>
 </safety-center-config>
-                """)),
-                (xml(
-                    "res/values/strings.xml",
-                    """
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="reference" translatable="false">Reference</string>
-</resources>
-                """)))
-            .run()
-            .expectClean()
-    }
+            """
 
-    @Test
-    fun invalidConfig_throws() {
-        lint()
-            .files((xml("res/raw/safety_center_config.xml", "<invalid-root/>")))
-            .run()
-            .expect(
-                "res/raw/safety_center_config.xml: Error: Parser exception: " +
-                    "\"Element safety-center-config missing\", cause: \"null\" " +
-                    "[InvalidSafetyCenterConfig]\n1 errors, 0 warnings")
-    }
-
-    @Test
-    fun unrelatedFile_doesNotThrow() {
-        lint()
-            .files((xml("res/raw/some_other_config.xml", "<some-other-root/>")))
-            .run()
-            .expectClean()
-    }
-
-    @Test
-    fun unrelatedFolder_doesNotThrow() {
-        lint().files((xml("res/values/strings.xml", "<some-other-root/>"))).run().expectClean()
+        const val VALID_UDC_CONFIG =
+            """
+<safety-center-config>
+    <safety-sources-config>
+        <safety-sources-group
+            id="group"
+            title="@lint.test.pkg:string/reference"
+            summary="@lint.test.pkg:string/reference">
+            <dynamic-safety-source
+                id="source"
+                packageName="package"
+                title="@lint.test.pkg:string/reference"
+                summary="@lint.test.pkg:string/reference"
+                intentAction="intent"
+                profile="primary_profile_only"
+                notificationsAllowed="true"/>
+        </safety-sources-group>
+    </safety-sources-config>
+</safety-center-config>
+            """
     }
 }
diff --git a/SafetyCenter/InternalData/java/com/android/safetycenter/internaldata/SafetyCenterIds.java b/SafetyCenter/InternalData/java/com/android/safetycenter/internaldata/SafetyCenterIds.java
index 44057b3..f22b328 100644
--- a/SafetyCenter/InternalData/java/com/android/safetycenter/internaldata/SafetyCenterIds.java
+++ b/SafetyCenter/InternalData/java/com/android/safetycenter/internaldata/SafetyCenterIds.java
@@ -36,17 +36,6 @@
     private SafetyCenterIds() {}
 
     /**
-     * Converts a String to a {@link SafetyCenterEntryGroupId}.
-     *
-     * <p>Throws an {@link IllegalArgumentException} if the String couldn't be converted to a {@link
-     * SafetyCenterEntryGroupId}.
-     */
-    @NonNull
-    public static SafetyCenterEntryGroupId entryGroupIdFromString(@NonNull String encoded) {
-        return decodeToProto(SafetyCenterEntryGroupId.parser(), encoded);
-    }
-
-    /**
      * Converts a String to a {@link SafetyCenterEntryId}.
      *
      * <p>Throws an {@link IllegalArgumentException} if the String couldn't be converted to a {@link
diff --git a/SafetyCenter/InternalData/proto/safety_center_internal_data.proto b/SafetyCenter/InternalData/proto/safety_center_internal_data.proto
index 676daf9..d9d6a21 100644
--- a/SafetyCenter/InternalData/proto/safety_center_internal_data.proto
+++ b/SafetyCenter/InternalData/proto/safety_center_internal_data.proto
@@ -20,12 +20,6 @@
 
 option java_multiple_files = true;
 
-// An id for entry groups provided by the Safety Center.
-message SafetyCenterEntryGroupId {
-  // The ID of the safety sources group that the entry group originated from.
-  optional string safety_sources_group_id = 1;
-}
-
 // An id for entries provided by the Safety Center.
 message SafetyCenterEntryId {
   // The ID of the safety source that the entry originated from.
diff --git a/SafetyCenter/Persistence/tests/AndroidTest.xml b/SafetyCenter/Persistence/tests/AndroidTest.xml
index 3a11886..007330b 100644
--- a/SafetyCenter/Persistence/tests/AndroidTest.xml
+++ b/SafetyCenter/Persistence/tests/AndroidTest.xml
@@ -21,7 +21,7 @@
     <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk33ModuleController" />
 
     <!-- Install test -->
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="test-file-name" value="SafetyCenterPersistenceTests.apk" />
         <option name="cleanup-apks" value="true" />
     </target_preparer>
diff --git a/SafetyCenter/Persistence/tests/java/com/android/safetycenter/persistence/PersistedSafetyCenterIssueTest.kt b/SafetyCenter/Persistence/tests/java/com/android/safetycenter/persistence/PersistedSafetyCenterIssueTest.kt
index 7f79437..34f8f53 100644
--- a/SafetyCenter/Persistence/tests/java/com/android/safetycenter/persistence/PersistedSafetyCenterIssueTest.kt
+++ b/SafetyCenter/Persistence/tests/java/com/android/safetycenter/persistence/PersistedSafetyCenterIssueTest.kt
@@ -53,7 +53,7 @@
 
     @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        EqualsHashCodeToStringTester.of<PersistedSafetyCenterIssue>()
             .addEqualityGroup(
                 ACTIVE_ISSUE,
                 PersistedSafetyCenterIssue.Builder()
diff --git a/SafetyCenter/Resources/res/raw-v34/safety_center_config.xml b/SafetyCenter/Resources/res/raw-v34/safety_center_config.xml
new file mode 100644
index 0000000..e9f4f91
--- /dev/null
+++ b/SafetyCenter/Resources/res/raw-v34/safety_center_config.xml
@@ -0,0 +1,111 @@
+<!--
+  ~ 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.
+  -->
+
+<safety-center-config>
+    <safety-sources-config>
+        <safety-sources-group
+            id="AndroidLockScreenSources"
+            title="@com.android.safetycenter.resources:string/lock_screen_sources_title"
+            summary="@com.android.safetycenter.resources:string/lock_screen_sources_summary">
+            <dynamic-safety-source
+                id="AndroidLockScreen"
+                packageName="com.android.settings"
+                profile="primary_profile_only"
+                title="@com.android.safetycenter.resources:string/lock_screen_title"
+                summary="@com.android.safetycenter.resources:string/lock_screen_summary_disabled"
+                searchTerms="@com.android.safetycenter.resources:string/lock_screen_search_terms"
+                initialDisplayState="disabled"/>
+            <dynamic-safety-source
+                id="AndroidBiometrics"
+                packageName="com.android.settings"
+                profile="primary_profile_only"
+                title="@com.android.safetycenter.resources:string/biometrics_title"
+                searchTerms="@com.android.safetycenter.resources:string/biometrics_search_terms"
+                initialDisplayState="hidden"/>
+        </safety-sources-group>
+        <safety-sources-group
+            id="AndroidPrivacySources"
+            title="@com.android.safetycenter.resources:string/privacy_sources_title"
+            summary="@com.android.safetycenter.resources:string/privacy_sources_summary"
+            statelessIconType="privacy">
+            <static-safety-source
+                id="AndroidPermissionUsage"
+                profile="primary_profile_only"
+                intentAction="android.intent.action.REVIEW_PERMISSION_USAGE"
+                title="@com.android.safetycenter.resources:string/permission_usage_title"
+                summary="@com.android.safetycenter.resources:string/permission_usage_summary"
+                searchTerms="@com.android.safetycenter.resources:string/permission_usage_search_terms"/>
+            <static-safety-source
+                id="AndroidPermissionManager"
+                profile="primary_profile_only"
+                intentAction="android.intent.action.MANAGE_PERMISSIONS"
+                title="@com.android.safetycenter.resources:string/permission_manager_title"
+                summary="@com.android.safetycenter.resources:string/permission_manager_summary"
+                searchTerms="@com.android.safetycenter.resources:string/permission_manager_search_terms"/>
+            <static-safety-source
+                id="AndroidPrivacyControls"
+                profile="primary_profile_only"
+                intentAction="android.settings.PRIVACY_CONTROLS"
+                title="@com.android.safetycenter.resources:string/privacy_controls_title"
+                summary="@com.android.safetycenter.resources:string/privacy_controls_summary"
+                searchTerms="@com.android.safetycenter.resources:string/privacy_controls_search_terms"/>
+            <issue-only-safety-source
+                id="AndroidAccessibility"
+                packageName="com.android.permissioncontroller"
+                profile="all_profiles"
+                refreshOnPageOpenAllowed="true"/>
+            <issue-only-safety-source
+                id="AndroidNotificationListener"
+                packageName="com.android.permissioncontroller"
+                profile="primary_profile_only"
+                refreshOnPageOpenAllowed="true"/>
+            <issue-only-safety-source
+                id="AndroidBackgroundLocation"
+                packageName="com.android.permissioncontroller"
+                profile="all_profiles"
+                refreshOnPageOpenAllowed="true"/>
+            <issue-only-safety-source
+                id="AndroidPermissionAutoRevoke"
+                packageName="com.android.permissioncontroller"
+                profile="all_profiles"
+                refreshOnPageOpenAllowed="true"/>
+        </safety-sources-group>
+        <safety-sources-group
+            id="AndroidAdvancedSources"
+            title="@com.android.safetycenter.resources:string/advanced_title">
+            <dynamic-safety-source
+                id="AndroidWorkPolicyInfo"
+                packageName="com.android.permissioncontroller"
+                profile="primary_profile_only"
+                initialDisplayState="hidden"
+                refreshOnPageOpenAllowed="true"/>
+            <static-safety-source
+                id="AndroidAdvancedSecurity"
+                profile="primary_profile_only"
+                intentAction="com.android.settings.security.SECURITY_ADVANCED_SETTINGS"
+                title="@com.android.safetycenter.resources:string/advanced_security_title"
+                summary="@com.android.safetycenter.resources:string/advanced_security_summary"
+                searchTerms="@com.android.safetycenter.resources:string/advanced_security_search_terms"/>
+            <static-safety-source
+                id="AndroidAdvancedPrivacy"
+                profile="primary_profile_only"
+                intentAction="android.settings.PRIVACY_ADVANCED_SETTINGS"
+                title="@com.android.safetycenter.resources:string/advanced_privacy_title"
+                summary="@com.android.safetycenter.resources:string/advanced_privacy_summary"
+                searchTerms="@com.android.safetycenter.resources:string/advanced_privacy_search_terms"/>
+        </safety-sources-group>
+    </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/Resources/res/values-af/strings.xml b/SafetyCenter/Resources/res/values-af/strings.xml
index 82b42f3..e46fb35 100644
--- a/SafetyCenter/Resources/res/values-af/strings.xml
+++ b/SafetyCenter/Resources/res/values-af/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Meer privaatheidinstellings"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Outovul, aktiwiteitkontroles, en meer"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Rekening is dalk in gevaar"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Rekening is in gevaar"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-am/strings.xml b/SafetyCenter/Resources/res/values-am/strings.xml
index 04c1b60..32e7fd8 100644
--- a/SafetyCenter/Resources/res/values-am/strings.xml
+++ b/SafetyCenter/Resources/res/values-am/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"ተጨማሪ የግላዊነት ቅንብሮች"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"ራስ-ሙላ፣ የእንቅስቃሴ መቆጣጠሪያዎች እና ሌሎችም"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"መለያ አደጋ ላይ ሊሆን ይችላል"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"መለያው አደጋ ላይ ነው"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-ar/strings.xml b/SafetyCenter/Resources/res/values-ar/strings.xml
index afa6a94..e0e0279 100644
--- a/SafetyCenter/Resources/res/values-ar/strings.xml
+++ b/SafetyCenter/Resources/res/values-ar/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"المزيد من إعدادات الخصوصية"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"الملء التلقائي وعناصر التحكُّم في النشاط وغير ذلك"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"قد يكون الحساب معرّضًا للخطر"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"الحساب معرّض للخطر"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-as/strings.xml b/SafetyCenter/Resources/res/values-as/strings.xml
index 1d30691..94bf132 100644
--- a/SafetyCenter/Resources/res/values-as/strings.xml
+++ b/SafetyCenter/Resources/res/values-as/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"গোপনীয়তাৰ অধিক ছেটিং"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"স্বয়ংক্ৰিয়ভাৱে পূৰ হোৱা সুবিধা, কাৰ্যকলাপৰ নিয়ন্ত্ৰণসমূহ আৰু বহুতো"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"একাউণ্টটোত বিপদাশংকা থাকিব পাৰে"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"একাউণ্টত বিপদাশংকা আছে"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-az/strings.xml b/SafetyCenter/Resources/res/values-az/strings.xml
index 1de651e..bf10a0f 100644
--- a/SafetyCenter/Resources/res/values-az/strings.xml
+++ b/SafetyCenter/Resources/res/values-az/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Digər məxfilik ayarları"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Avto doldurma, fəaliyyətə nəzarət və s."</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Hesab risk altında ola bilər"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Hesab risk altındadır"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-b+sr+Latn/strings.xml b/SafetyCenter/Resources/res/values-b+sr+Latn/strings.xml
index 97dcc18..6072868 100644
--- a/SafetyCenter/Resources/res/values-b+sr+Latn/strings.xml
+++ b/SafetyCenter/Resources/res/values-b+sr+Latn/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Još podešavanja privatnosti"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Automatsko popunjavanje, kontrole aktivnosti i drugo"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Nalog je možda ugrožen"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Nalog je ugrožen"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-be/strings.xml b/SafetyCenter/Resources/res/values-be/strings.xml
index 28437b3..d62a5c2 100644
--- a/SafetyCenter/Resources/res/values-be/strings.xml
+++ b/SafetyCenter/Resources/res/values-be/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Іншыя налады прыватнасці"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Аўтазапаўненне, адсочванне дзеянняў і іншыя функцыі"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Уліковы запіс можа быць у небяспецы"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Уліковы запіс у небяспецы"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-bg/strings.xml b/SafetyCenter/Resources/res/values-bg/strings.xml
index 02e3983..b9029d2 100644
--- a/SafetyCenter/Resources/res/values-bg/strings.xml
+++ b/SafetyCenter/Resources/res/values-bg/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Още настройки за поверителност"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Автоматично попълване, контроли за активността и др."</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Профилът може да е изложен на риск"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Профилът е изложен на риск"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-bn/strings.xml b/SafetyCenter/Resources/res/values-bn/strings.xml
index 916641e..012f830 100644
--- a/SafetyCenter/Resources/res/values-bn/strings.xml
+++ b/SafetyCenter/Resources/res/values-bn/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"আরও গোপনীয়তা সেটিংস"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"অটোফিল, অ্যাক্টিভিটি নিয়ন্ত্রণ এবং আরও অনেক কিছু"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"আপনার অ্যাকাউন্ট হয়ত সুরক্ষিত নয়"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"অ্যাকাউন্টের ক্ষতি হতে পারে"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-bs/strings.xml b/SafetyCenter/Resources/res/values-bs/strings.xml
index 02837a2..f24d641 100644
--- a/SafetyCenter/Resources/res/values-bs/strings.xml
+++ b/SafetyCenter/Resources/res/values-bs/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Više postavki privatnosti"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Automatsko popunjavanje, kontrole aktivnosti i drugo"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Račun bi mogao biti izložen riziku"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Račun je izložen riziku"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-ca/strings.xml b/SafetyCenter/Resources/res/values-ca/strings.xml
index cf8705b..950d0b7 100644
--- a/SafetyCenter/Resources/res/values-ca/strings.xml
+++ b/SafetyCenter/Resources/res/values-ca/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Més opcions de configuració de privadesa"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Emplenament automàtic, controls d\'activitat i més"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"El compte pot estar en risc"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Aquest compte està en perill"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-cs/strings.xml b/SafetyCenter/Resources/res/values-cs/strings.xml
index 6185a79..a980c78 100644
--- a/SafetyCenter/Resources/res/values-cs/strings.xml
+++ b/SafetyCenter/Resources/res/values-cs/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Další nastavení ochrany soukromí"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Automatické vyplňování, ovládací prvky aktivity a další"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Účet může být ohrožen"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Účet je v ohrožení"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-da/strings.xml b/SafetyCenter/Resources/res/values-da/strings.xml
index 9f37d08..3f6bf68 100644
--- a/SafetyCenter/Resources/res/values-da/strings.xml
+++ b/SafetyCenter/Resources/res/values-da/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Flere privatlivsindstillinger"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Autofyld, aktivitetsadministration og mere"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Kontoen kan være sårbar"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Kontoen er sårbar"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-de/strings.xml b/SafetyCenter/Resources/res/values-de/strings.xml
index 62b8f2a..2e33657 100644
--- a/SafetyCenter/Resources/res/values-de/strings.xml
+++ b/SafetyCenter/Resources/res/values-de/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Weitere Datenschutzeinstellungen"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Autofill, Aktivitätseinstellungen und mehr"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Konto eventuell gefährdet"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Konto ist gefährdet"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-el/strings.xml b/SafetyCenter/Resources/res/values-el/strings.xml
index 02ff2c1..19e0a3b 100644
--- a/SafetyCenter/Resources/res/values-el/strings.xml
+++ b/SafetyCenter/Resources/res/values-el/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Περισσότερες ρυθμίσεις απορρήτου"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Αυτόματη συμπλήρωση, στοιχεία ελέγχου δραστηριότητας και περισσότερα"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Ο λογαριασμός μπορεί να κινδυνεύει"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Ο λογαριασμός βρίσκεται σε κίνδυνο"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-en-rAU/strings.xml b/SafetyCenter/Resources/res/values-en-rAU/strings.xml
index 8cf663e..79d5386 100644
--- a/SafetyCenter/Resources/res/values-en-rAU/strings.xml
+++ b/SafetyCenter/Resources/res/values-en-rAU/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"More privacy settings"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Auto-fill, Activity controls and more"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Account may be at risk"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Account is at risk"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-en-rCA/strings.xml b/SafetyCenter/Resources/res/values-en-rCA/strings.xml
index 8cf663e..813a090 100644
--- a/SafetyCenter/Resources/res/values-en-rCA/strings.xml
+++ b/SafetyCenter/Resources/res/values-en-rCA/strings.xml
@@ -17,7 +17,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="safetyCenterResourcesAppLabel" msgid="4043334186295695930">"Safety Centre resources"</string>
+    <string name="safetyCenterResourcesAppLabel" msgid="4043334186295695930">"Safety Center Resources"</string>
     <string name="lock_screen_sources_title" msgid="3317906280484627707">"Device lock"</string>
     <string name="lock_screen_sources_summary" msgid="7220439741282516496"></string>
     <string name="lock_screen_title" msgid="4069104894527169877">"Screen lock"</string>
@@ -34,15 +34,13 @@
     <string name="permission_manager_summary" msgid="8099852107340970790">"Control app access to your data"</string>
     <string name="permission_manager_search_terms" msgid="2895147613099694722">"Permissions, Permissions manager"</string>
     <string name="privacy_controls_title" msgid="5322875777945432395">"Privacy controls"</string>
-    <string name="privacy_controls_summary" msgid="2402066941190435424">"Control device access to microphone, camera and more"</string>
+    <string name="privacy_controls_summary" msgid="2402066941190435424">"Control device access to microphone, camera, and more"</string>
     <string name="privacy_controls_search_terms" msgid="3774472175934304165">"Privacy, Privacy controls"</string>
     <string name="advanced_title" msgid="8745436380690561172">"More settings"</string>
     <string name="advanced_security_title" msgid="1126833338772188155">"More security settings"</string>
-    <string name="advanced_security_summary" msgid="6172253327022425123">"Encryption, credentials and more"</string>
+    <string name="advanced_security_summary" msgid="6172253327022425123">"Encryption, credentials, and more"</string>
     <string name="advanced_security_search_terms" msgid="3350609555814362075"></string>
     <string name="advanced_privacy_title" msgid="1117725225706176643">"More privacy settings"</string>
-    <string name="advanced_privacy_summary" msgid="2281203390575069543">"Auto-fill, Activity controls and more"</string>
+    <string name="advanced_privacy_summary" msgid="2281203390575069543">"Autofill, activity controls, and more"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Account may be at risk"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Account is at risk"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-en-rGB/strings.xml b/SafetyCenter/Resources/res/values-en-rGB/strings.xml
index 8cf663e..79d5386 100644
--- a/SafetyCenter/Resources/res/values-en-rGB/strings.xml
+++ b/SafetyCenter/Resources/res/values-en-rGB/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"More privacy settings"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Auto-fill, Activity controls and more"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Account may be at risk"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Account is at risk"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-en-rIN/strings.xml b/SafetyCenter/Resources/res/values-en-rIN/strings.xml
index 8cf663e..79d5386 100644
--- a/SafetyCenter/Resources/res/values-en-rIN/strings.xml
+++ b/SafetyCenter/Resources/res/values-en-rIN/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"More privacy settings"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Auto-fill, Activity controls and more"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Account may be at risk"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Account is at risk"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-en-rXC/strings.xml b/SafetyCenter/Resources/res/values-en-rXC/strings.xml
index 813606c..66e9e13 100644
--- a/SafetyCenter/Resources/res/values-en-rXC/strings.xml
+++ b/SafetyCenter/Resources/res/values-en-rXC/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‎‏‎‎‏‎‏‎‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‎‎‏‏‎More privacy settings‎‏‎‎‏‎"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‏‎‏‏‎‎‏‏‎‎‏‎‏‏‎‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‎‎‏‎‏‏‎‎‏‏‏‎Autofill, activity controls, and more‎‏‎‎‏‎"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‎‎‎‏‎‎‎‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‏‎Account may be at risk‎‏‎‎‏‎"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‏‎‏‎‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‏‎‏‏‎‏‏‏‏‎‏‎‏‎‏‎Account is at risk‎‏‎‎‏‎"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-es-rUS/strings.xml b/SafetyCenter/Resources/res/values-es-rUS/strings.xml
index 7a67f92..5c1b274 100644
--- a/SafetyCenter/Resources/res/values-es-rUS/strings.xml
+++ b/SafetyCenter/Resources/res/values-es-rUS/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Más parámetros de privacidad"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Autocompletar, controles de actividad y más"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"La cuenta podría estar en peligro"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"La cuenta está en riesgo"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-es/strings.xml b/SafetyCenter/Resources/res/values-es/strings.xml
index 5a9a928..6f7f380 100644
--- a/SafetyCenter/Resources/res/values-es/strings.xml
+++ b/SafetyCenter/Resources/res/values-es/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Más ajustes de privacidad"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Autocompletar, controles de actividad y más"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"La cuenta puede correr peligro"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"La cuenta está en riesgo"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-et/strings.xml b/SafetyCenter/Resources/res/values-et/strings.xml
index 504e729..a7ff482 100644
--- a/SafetyCenter/Resources/res/values-et/strings.xml
+++ b/SafetyCenter/Resources/res/values-et/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Rohkem privaatsusseadeid"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Automaattäide, kontotegevuste haldus ja muu"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Konto võib olla ohus"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Konto on ohus"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-eu/strings.xml b/SafetyCenter/Resources/res/values-eu/strings.xml
index 067aa56..78d067d 100644
--- a/SafetyCenter/Resources/res/values-eu/strings.xml
+++ b/SafetyCenter/Resources/res/values-eu/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Pribatutasun-ezarpen gehiago"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Betetze automatikoa, jarduerak kontrolatzeko aukerak eta abar"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Baliteke kontua arriskuan egotea"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Kontua arriskuan dago"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-fa/strings.xml b/SafetyCenter/Resources/res/values-fa/strings.xml
index c2c5bc7..868f9bf 100644
--- a/SafetyCenter/Resources/res/values-fa/strings.xml
+++ b/SafetyCenter/Resources/res/values-fa/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"تنظیمات حریم خصوصی بیشتر"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"تکمیل خودکار، کنترل‌های فعالیت، و غیره"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"ممکن است حساب درمعرض خطر باشد"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"حساب درمعرض خطر است"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-fi/strings.xml b/SafetyCenter/Resources/res/values-fi/strings.xml
index 0a36343..de46fb2 100644
--- a/SafetyCenter/Resources/res/values-fi/strings.xml
+++ b/SafetyCenter/Resources/res/values-fi/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Lisää yksityisyysasetuksia"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Automaattinen täyttö, toimintojen hallinta ja muuta"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Tili voi olla vaarantunut"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Tili on vaarantunut"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-fr-rCA/strings.xml b/SafetyCenter/Resources/res/values-fr-rCA/strings.xml
index 2e19967..1ae13ac 100644
--- a/SafetyCenter/Resources/res/values-fr-rCA/strings.xml
+++ b/SafetyCenter/Resources/res/values-fr-rCA/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Plus de paramètres de sécurité"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Remplissage automatique, commandes d\'activité et plus"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Le compte pourrait être à risque"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Le compte est en danger"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-fr/strings.xml b/SafetyCenter/Resources/res/values-fr/strings.xml
index e30fabb..ef0d893 100644
--- a/SafetyCenter/Resources/res/values-fr/strings.xml
+++ b/SafetyCenter/Resources/res/values-fr/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Autres paramètres de confidentialité"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Saisie automatique, commandes relatives à l\'activité et bien plus"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Risque potentiel sur le compte"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Risque sur le compte"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-gl/strings.xml b/SafetyCenter/Resources/res/values-gl/strings.xml
index 66abd41..7ae87b9 100644
--- a/SafetyCenter/Resources/res/values-gl/strings.xml
+++ b/SafetyCenter/Resources/res/values-gl/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Máis opcións de configuración de privacidade"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Autocompletar, controis de actividade e moito máis"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"A conta pode estar en risco"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"A conta está en risco"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-gu/strings.xml b/SafetyCenter/Resources/res/values-gu/strings.xml
index 6df6805..e44aa5b 100644
--- a/SafetyCenter/Resources/res/values-gu/strings.xml
+++ b/SafetyCenter/Resources/res/values-gu/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"વધુ પ્રાઇવસી સેટિંગ"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"આપમેળે ભરવાની સુવિધા, ઍક્ટિવિટી કન્ટ્રોલ અને વધુ"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"એકાઉન્ટ જોખમમાં હોઈ શકે છે"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"એકાઉન્ટ જોખમમાં છે"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-hi/strings.xml b/SafetyCenter/Resources/res/values-hi/strings.xml
index 7965de6..419f775 100644
--- a/SafetyCenter/Resources/res/values-hi/strings.xml
+++ b/SafetyCenter/Resources/res/values-hi/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"निजता से जुड़ी और सेटिंग"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"ऑटोमैटिक जानकारी भरना, गतिविधि कंट्रोल वगैरह"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"खाता खतरे में हो सकता है"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"खाते की सुरक्षा खतरे में है"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-hr/strings.xml b/SafetyCenter/Resources/res/values-hr/strings.xml
index bdc474e..a4c43ad 100644
--- a/SafetyCenter/Resources/res/values-hr/strings.xml
+++ b/SafetyCenter/Resources/res/values-hr/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Više postavki privatnosti"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Automatsko popunjavanje, kontrole aktivnosti i ostalo"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Račun je možda ugrožen"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Račun je ugrožen"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-hu/strings.xml b/SafetyCenter/Resources/res/values-hu/strings.xml
index 1c234d0..e0a7087 100644
--- a/SafetyCenter/Resources/res/values-hu/strings.xml
+++ b/SafetyCenter/Resources/res/values-hu/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"További adatvédelmi beállítások"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Automatikus kitöltés, tevékenységvezérlők és egyebek"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Fiókja veszélyben lehet"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Fiókja veszélyben van"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-hy/strings.xml b/SafetyCenter/Resources/res/values-hy/strings.xml
index 719c335..426ebfa 100644
--- a/SafetyCenter/Resources/res/values-hy/strings.xml
+++ b/SafetyCenter/Resources/res/values-hy/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Գաղտնիության այլ կարգավորումներ"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Ինքնալրացում, գործողությունների հետագծում և ավելին"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Հաշիվը կարող է վտանգված լինել"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Հաշիվը վտանգված է"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-in/strings.xml b/SafetyCenter/Resources/res/values-in/strings.xml
index 7ffacfa..4c3c210 100644
--- a/SafetyCenter/Resources/res/values-in/strings.xml
+++ b/SafetyCenter/Resources/res/values-in/strings.xml
@@ -24,7 +24,7 @@
     <string name="lock_screen_summary_disabled" msgid="354071230916616692">"Belum ada info"</string>
     <string name="lock_screen_search_terms" msgid="2678486357779794826">"Penguncian perangkat, Kunci layar, Layar kunci, Layar saat dikunci, Sandi, PIN, Pola"</string>
     <string name="biometrics_title" msgid="5859504610285212938">"Biometrik"</string>
-    <string name="biometrics_search_terms" msgid="6040319118762671981">"Sidik jari, Jari, Tambahkan sidik jari, Face unlock, Wajah"</string>
+    <string name="biometrics_search_terms" msgid="6040319118762671981">"Sidik jari, Jari, Tambahkan sidik jari, Buka dengan wajah, Wajah"</string>
     <string name="privacy_sources_title" msgid="4061110826457365957">"Privasi"</string>
     <string name="privacy_sources_summary" msgid="4089719981155120864">"Dasbor, izin, kontrol"</string>
     <string name="permission_usage_title" msgid="3633779688945350407">"Dasbor privasi"</string>
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Setelan privasi lainnya"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Isi otomatis, kontrol aktivitas, dan lainnya"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Akun mungkin berisiko"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Akun berisiko"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-is/strings.xml b/SafetyCenter/Resources/res/values-is/strings.xml
index c95fbc1..c462e5c 100644
--- a/SafetyCenter/Resources/res/values-is/strings.xml
+++ b/SafetyCenter/Resources/res/values-is/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Fleiri persónuverndarstillingar"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Sjálfvirk útfylling, virknistýringar og fleira"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Reikningurinn er hugsanlega í hættu"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Reikingurinn er í hættu"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-it/strings.xml b/SafetyCenter/Resources/res/values-it/strings.xml
index 68cdac9..b3ff166 100644
--- a/SafetyCenter/Resources/res/values-it/strings.xml
+++ b/SafetyCenter/Resources/res/values-it/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Altre impostazioni della privacy"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Compilazione automatica, Gestione attività e altro"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"L\'account potrebbe essere a rischio"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"L\'account è a rischio"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-iw/strings.xml b/SafetyCenter/Resources/res/values-iw/strings.xml
index eac31ac..bde0d29 100644
--- a/SafetyCenter/Resources/res/values-iw/strings.xml
+++ b/SafetyCenter/Resources/res/values-iw/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"הגדרות פרטיות נוספות"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"מילוי אוטומטי, בקרת הפעילות בחשבון ועוד"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"החשבון עלול להיות בסיכון"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"החשבון בסיכון"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-ja/strings.xml b/SafetyCenter/Resources/res/values-ja/strings.xml
index 93a58ca..6e67998 100644
--- a/SafetyCenter/Resources/res/values-ja/strings.xml
+++ b/SafetyCenter/Resources/res/values-ja/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"プライバシーの詳細設定"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"自動入力、アクティビティ管理など"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"アカウントが危険な可能性があります"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"アカウントが危険です"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-ka/strings.xml b/SafetyCenter/Resources/res/values-ka/strings.xml
index 8b119f8..af3e7cd 100644
--- a/SafetyCenter/Resources/res/values-ka/strings.xml
+++ b/SafetyCenter/Resources/res/values-ka/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"კონფიდენციალურობის დამატებითი პარამეტრები"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"ავტომატური შევსება, აქტივობის მართვის საშუალებები და სხვა"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"ანგარიშს შესაძლოა საფრთხე ემუქრება"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"ანგარიში საფრთხეშია"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-kk/strings.xml b/SafetyCenter/Resources/res/values-kk/strings.xml
index bcbc795..ee90db6 100644
--- a/SafetyCenter/Resources/res/values-kk/strings.xml
+++ b/SafetyCenter/Resources/res/values-kk/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Қосымша құпиялылық параметрлері"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Автотолтыру, іс-әрекетті басқару элементтері және т. б."</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Аккаунтқа қауіп төнген сияқты"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Аккаунтқа қауіп төніп тұр"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-km/strings.xml b/SafetyCenter/Resources/res/values-km/strings.xml
index 490760e..898f533 100644
--- a/SafetyCenter/Resources/res/values-km/strings.xml
+++ b/SafetyCenter/Resources/res/values-km/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"ការកំណត់ឯកជនភាព​ច្រើនទៀត"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"បំពេញស្វ័យប្រវត្តិ ការត្រួតពិនិត្យសកម្មភាព និងអ្វីៗជាច្រើនទៀត"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"គណនីអាចប្រឈមនឹងហានិភ័យ"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"គណនីប្រឈមនឹងហានិភ័យ"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-kn/strings.xml b/SafetyCenter/Resources/res/values-kn/strings.xml
index 31a039c..2e912b4 100644
--- a/SafetyCenter/Resources/res/values-kn/strings.xml
+++ b/SafetyCenter/Resources/res/values-kn/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"ಇನ್ನಷ್ಟು ಗೌಪ್ಯತೆ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"ಸ್ವಯಂ ಭರ್ತಿ, ಚಟುವಟಿಕೆ ನಿಯಂತ್ರಣಗಳು ಹಾಗೂ ಇನ್ನಷ್ಟು"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"ಖಾತೆಯು ಅಪಾಯಕ್ಕೀಡಾಗಬಹುದು"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"ಖಾತೆ ಅಪಾಯದಲ್ಲಿದೆ"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-ko/strings.xml b/SafetyCenter/Resources/res/values-ko/strings.xml
index 872a5ee..307f69d 100644
--- a/SafetyCenter/Resources/res/values-ko/strings.xml
+++ b/SafetyCenter/Resources/res/values-ko/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"기타 개인 정보 보호 설정"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"자동 완성, 활동 제어 등"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"계정에 보안 위험이 있을 수 있음"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"계정이 위험합니다"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-ky/strings.xml b/SafetyCenter/Resources/res/values-ky/strings.xml
index ab21919..ecacf50 100644
--- a/SafetyCenter/Resources/res/values-ky/strings.xml
+++ b/SafetyCenter/Resources/res/values-ky/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Кошумча купуялык параметрлери"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Автотолтуруу, аракеттерге көз салуу жана башкалар"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Аккаунт коркунучта болушу мүмкүн"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Аккаунттун коопсуздугу коркунучта"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-lo/strings.xml b/SafetyCenter/Resources/res/values-lo/strings.xml
index a37faae..44f09d4 100644
--- a/SafetyCenter/Resources/res/values-lo/strings.xml
+++ b/SafetyCenter/Resources/res/values-lo/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"ການຕັ້ງຄ່າຄວາມເປັນສ່ວນຕົວເພີ່ມເຕີມ"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"ການຕື່ມຂໍ້ມູນອັດຕະໂນມັດ, ການຄວບຄຸມການເຄື່ອນໄຫວ ແລະ ອື່ນໆ"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"ບັນຊີອາດຕົກຢູ່ໃນຄວາມສ່ຽ"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"ບັນຊີມີຄວາມສ່ຽງ"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-lt/strings.xml b/SafetyCenter/Resources/res/values-lt/strings.xml
index 20a8c6f..349ee74 100644
--- a/SafetyCenter/Resources/res/values-lt/strings.xml
+++ b/SafetyCenter/Resources/res/values-lt/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Daugiau privatumo nustatymų"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Automatinis pildymas, veiklos valdikliai ir kt."</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Paskyrai galėjo iškilti pavojus"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Paskyrai iškilo pavojus"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-lv/strings.xml b/SafetyCenter/Resources/res/values-lv/strings.xml
index 6496237..b6e7a32 100644
--- a/SafetyCenter/Resources/res/values-lv/strings.xml
+++ b/SafetyCenter/Resources/res/values-lv/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Citi konfidencialitātes iestatījumi"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Automātiskā aizpilde, aktivitātes vadīklas un citas iestatījumi"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Konts var būt apdraudēts"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Konts ir apdraudēts"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-mk/strings.xml b/SafetyCenter/Resources/res/values-mk/strings.xml
index 174ec95..bc3dbfd 100644
--- a/SafetyCenter/Resources/res/values-mk/strings.xml
+++ b/SafetyCenter/Resources/res/values-mk/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Повеќе поставки за приватност"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Автоматско пополнување, контроли на активноста и друго"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Сметката можеби е изложена на ризик"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Сметката е под ризик"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-ml/strings.xml b/SafetyCenter/Resources/res/values-ml/strings.xml
index e337584..cec3302 100644
--- a/SafetyCenter/Resources/res/values-ml/strings.xml
+++ b/SafetyCenter/Resources/res/values-ml/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"കൂടുതൽ സ്വകാര്യതാ ക്രമീകരണം"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"സ്വയമേവ പൂരിപ്പിക്കൽ, ആക്റ്റിവിറ്റി നിയന്ത്രണങ്ങൾ എന്നിവയും മറ്റും"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"അക്കൗണ്ട് അപകടത്തിലായേക്കാം"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"അക്കൗണ്ട് അപകടത്തിലാണ്"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-mn/strings.xml b/SafetyCenter/Resources/res/values-mn/strings.xml
index 78070aa..e7c9516 100644
--- a/SafetyCenter/Resources/res/values-mn/strings.xml
+++ b/SafetyCenter/Resources/res/values-mn/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Нууцлалын бусад тохиргоо"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Автоматаар бөглөх хэсэг, үйл ажиллагааны хяналт болон бусад"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Бүртгэл эрсдэлд байж магадгүй"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Бүртгэл эрсдэлд байна"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-mr/strings.xml b/SafetyCenter/Resources/res/values-mr/strings.xml
index 16bb7c4..86fc17f 100644
--- a/SafetyCenter/Resources/res/values-mr/strings.xml
+++ b/SafetyCenter/Resources/res/values-mr/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"आणखी गोपनीयता सेटिंग्ज"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"ऑटोफिल, अ‍ॅक्टिव्हिटी कंट्रोल आणि आणखी बरेच काही"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"खाते धोक्यात असू शकते"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"खाते धोक्यात आहे"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-ms/strings.xml b/SafetyCenter/Resources/res/values-ms/strings.xml
index 14964d3..a105803 100644
--- a/SafetyCenter/Resources/res/values-ms/strings.xml
+++ b/SafetyCenter/Resources/res/values-ms/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Lagi tetapan privasi"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Autolengkap, kawalan aktiviti dan banyak lagi"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Akaun mungkin berisiko"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Akaun berisiko"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-my/strings.xml b/SafetyCenter/Resources/res/values-my/strings.xml
index 35daec9..d0d895d 100644
--- a/SafetyCenter/Resources/res/values-my/strings.xml
+++ b/SafetyCenter/Resources/res/values-my/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"နောက်ထပ် ကိုယ်ရေးအချက်အလက်လုံခြုံမှု ဆက်တင်များ"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"အော်တိုဖြည့်၊ လုပ်ဆောင်ချက် ထိန်းချုပ်မှုများ စသည်"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"အကောင့်တွင် အန္တရာယ်ရှိနိုင်သည်"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"အကောင့်တွင် အန္တရာယ်ရှိနေသည်"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-nb/strings.xml b/SafetyCenter/Resources/res/values-nb/strings.xml
index 0176672..15c311f 100644
--- a/SafetyCenter/Resources/res/values-nb/strings.xml
+++ b/SafetyCenter/Resources/res/values-nb/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Flere personverninnstillinger"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Autofyll, aktivitetslagring med mer"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Kontoen kan være i faresonen"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Kontoen er i faresonen"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-ne/strings.xml b/SafetyCenter/Resources/res/values-ne/strings.xml
index 2837c8e..5ceab04 100644
--- a/SafetyCenter/Resources/res/values-ne/strings.xml
+++ b/SafetyCenter/Resources/res/values-ne/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"गोपनीयतासम्बन्धी थप सेटिङ"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"अटोफिल, गतिविधिसम्बन्धी सेटिङ र अन्य कुराहरू"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"खाता जोखिममा हुन सक्छ"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"खाता जोखिममा छ"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-nl/strings.xml b/SafetyCenter/Resources/res/values-nl/strings.xml
index 1577109..068dc5b 100644
--- a/SafetyCenter/Resources/res/values-nl/strings.xml
+++ b/SafetyCenter/Resources/res/values-nl/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Meer privacyinstellingen"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Automatisch invullen, activiteitsopties en meer"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Account loopt misschien gevaar"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Account loopt risico"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-or/strings.xml b/SafetyCenter/Resources/res/values-or/strings.xml
index 2e8369b..2d3ffb6 100644
--- a/SafetyCenter/Resources/res/values-or/strings.xml
+++ b/SafetyCenter/Resources/res/values-or/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"ଅଧିକ ଗୋପନୀୟତା ସେଟିଂସ"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"ଅଟୋଫିଲ, କାର୍ଯ୍ୟକଳାପ ନିୟନ୍ତ୍ରଣ ଏବଂ ଆହୁରି ଅନେକ କିଛି"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"ଆକାଉଣ୍ଟ ବିପଦରେ ଥାଇପାରେ"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"ଆକାଉଣ୍ଟ ରିସ୍କରେ ଅଛି"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-pa/strings.xml b/SafetyCenter/Resources/res/values-pa/strings.xml
index 8c4f94f..4a381c1 100644
--- a/SafetyCenter/Resources/res/values-pa/strings.xml
+++ b/SafetyCenter/Resources/res/values-pa/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"ਹੋਰ ਪਰਦੇਦਾਰੀ ਸੈਟਿੰਗਾਂ"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"ਆਟੋਫਿਲ, ਸਰਗਰਮੀ ਕੰਟਰੋਲ ਅਤੇ ਹੋਰ ਬਹੁਤ ਕੁਝ"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"ਖਾਤਾ ਜੋਖਮ ਵਿੱਚ ਹੋ ਸਕਦਾ ਹੈ"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"ਖਾਤਾ ਜੋਖਮ ਵਿੱਚ ਹੈ"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-pl/strings.xml b/SafetyCenter/Resources/res/values-pl/strings.xml
index 878fdb3..6d5bf43 100644
--- a/SafetyCenter/Resources/res/values-pl/strings.xml
+++ b/SafetyCenter/Resources/res/values-pl/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Więcej ustawień prywatności"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Autouzupełnianie, zarządzanie aktywnością i inne"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Konto może być zagrożone"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Konto jest zagrożone"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-pt-rBR/strings.xml b/SafetyCenter/Resources/res/values-pt-rBR/strings.xml
index d090c59..ea35626 100644
--- a/SafetyCenter/Resources/res/values-pt-rBR/strings.xml
+++ b/SafetyCenter/Resources/res/values-pt-rBR/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Mais configurações de privacidade"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Preenchimento automático, controles de atividade e muito mais"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"A conta pode estar em risco"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Conta em risco"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-pt-rPT/strings.xml b/SafetyCenter/Resources/res/values-pt-rPT/strings.xml
index c2b6b7b..80fda0d 100644
--- a/SafetyCenter/Resources/res/values-pt-rPT/strings.xml
+++ b/SafetyCenter/Resources/res/values-pt-rPT/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Mais definições de privacidade"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Preenchimento automático, controlos da atividade e mais"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"A conta pode estar em risco"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"A conta está em risco"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-pt/strings.xml b/SafetyCenter/Resources/res/values-pt/strings.xml
index d090c59..ea35626 100644
--- a/SafetyCenter/Resources/res/values-pt/strings.xml
+++ b/SafetyCenter/Resources/res/values-pt/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Mais configurações de privacidade"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Preenchimento automático, controles de atividade e muito mais"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"A conta pode estar em risco"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Conta em risco"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-ro/strings.xml b/SafetyCenter/Resources/res/values-ro/strings.xml
index bdeda9a..20bf923 100644
--- a/SafetyCenter/Resources/res/values-ro/strings.xml
+++ b/SafetyCenter/Resources/res/values-ro/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Mai multe setări de confidențialitate"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Completare automată, opțiuni privind activitatea și altele"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Contul poate fi în pericol"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Contul este în pericol"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-ru/strings.xml b/SafetyCenter/Resources/res/values-ru/strings.xml
index f0a7e7c..aaa71ae 100644
--- a/SafetyCenter/Resources/res/values-ru/strings.xml
+++ b/SafetyCenter/Resources/res/values-ru/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Дополнительные настройки конфиденциальности"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Автозаполнение, отслеживание действий и прочее"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Аккаунт может быть под угрозой"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Аккаунт под угрозой"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-si/strings.xml b/SafetyCenter/Resources/res/values-si/strings.xml
index f879373..a8d8ee0 100644
--- a/SafetyCenter/Resources/res/values-si/strings.xml
+++ b/SafetyCenter/Resources/res/values-si/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"තවත් පෞද්ගලිකත්ව සැකසීම්"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"ස්වයං පිරවුම, ක්‍රියාකාරකම් පාලන සහ තවත් දේ"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"ගිණුම අවදානමේ තිබිය හැක"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"ගිණුම අවදානමේ පවතී"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-sk/strings.xml b/SafetyCenter/Resources/res/values-sk/strings.xml
index 402f5a9..6357114 100644
--- a/SafetyCenter/Resources/res/values-sk/strings.xml
+++ b/SafetyCenter/Resources/res/values-sk/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Ďalšie nastavenia ochrany súkromia"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Automatické dopĺňanie, Riadenie aktivity a ďalšie"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Účet môže byť ohrozený"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Účet je ohrozený"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-sl/strings.xml b/SafetyCenter/Resources/res/values-sl/strings.xml
index fa99cbb..d7b1c5c 100644
--- a/SafetyCenter/Resources/res/values-sl/strings.xml
+++ b/SafetyCenter/Resources/res/values-sl/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Več nastavitev zasebnosti"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Samodejno izpolnjevanje, kontrolniki za dejavnost in drugo"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Račun je morda ogrožen"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Račun je ogožen"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-sq/strings.xml b/SafetyCenter/Resources/res/values-sq/strings.xml
index 9f26e97..8671f53 100644
--- a/SafetyCenter/Resources/res/values-sq/strings.xml
+++ b/SafetyCenter/Resources/res/values-sq/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Cilësime të tjera të privatësisë"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Plotësimi automatik, kontrollet e aktivitetit dhe të tjera"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Llogaria mund të jetë në rrezik"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Llogaria është në rrezik"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-sr/strings.xml b/SafetyCenter/Resources/res/values-sr/strings.xml
index 28c11b6..4642ec7 100644
--- a/SafetyCenter/Resources/res/values-sr/strings.xml
+++ b/SafetyCenter/Resources/res/values-sr/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Још подешавања приватности"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Аутоматско попуњавање, контроле активности и друго"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Налог је можда угрожен"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Налог је угрожен"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-sv/strings.xml b/SafetyCenter/Resources/res/values-sv/strings.xml
index 28879b2..e10abbf 100644
--- a/SafetyCenter/Resources/res/values-sv/strings.xml
+++ b/SafetyCenter/Resources/res/values-sv/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Fler integritetsinställningar"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Autofyll, aktivitetsinställningar med mera"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Kontot kan vara i fara"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Kontot är i fara"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-sw/strings.xml b/SafetyCenter/Resources/res/values-sw/strings.xml
index dd322b4..5b1bd81 100644
--- a/SafetyCenter/Resources/res/values-sw/strings.xml
+++ b/SafetyCenter/Resources/res/values-sw/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Mipangilio zaidi ya faragha"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Kujaza kiotomatiki, vidhibiti vya shughuli na zaidi"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Huenda akaunti iko hatarini"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Akaunti iko hatarini"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-ta/strings.xml b/SafetyCenter/Resources/res/values-ta/strings.xml
index 21b2758..3586280 100644
--- a/SafetyCenter/Resources/res/values-ta/strings.xml
+++ b/SafetyCenter/Resources/res/values-ta/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"கூடுதல் தனியுரிமை அமைப்புகள்"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"தன்னிரப்பி, செயல்பாட்டுக் கட்டுப்பாடுகள் மற்றும் பல"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"கணக்கு ஆபத்தில் இருக்கலாம்"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"கணக்கு ஆபத்தில் உள்ளது"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-te/strings.xml b/SafetyCenter/Resources/res/values-te/strings.xml
index 8f41934..6763b16 100644
--- a/SafetyCenter/Resources/res/values-te/strings.xml
+++ b/SafetyCenter/Resources/res/values-te/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"మరిన్ని గోప్యతా సెట్టింగ్‌లు"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"ఆటోఫిల్, యాక్టీవిటీ కంట్రోల్స్, ఇంకా మరిన్ని"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"ఖాతా ప్రమాదంలో ఉండవచ్చు"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"ఖాతా ప్రమాదంలో ఉంది"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-th/strings.xml b/SafetyCenter/Resources/res/values-th/strings.xml
index 641b927..63656d7 100644
--- a/SafetyCenter/Resources/res/values-th/strings.xml
+++ b/SafetyCenter/Resources/res/values-th/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"การตั้งค่าความเป็นส่วนตัวเพิ่มเติม"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"ป้อนข้อความอัตโนมัติ ส่วนควบคุมกิจกรรม และอื่นๆ"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"บัญชีอาจมีความเสี่ยง"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"บัญชีมีความเสี่ยง"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-tl/strings.xml b/SafetyCenter/Resources/res/values-tl/strings.xml
index 88ea319..63b0abf 100644
--- a/SafetyCenter/Resources/res/values-tl/strings.xml
+++ b/SafetyCenter/Resources/res/values-tl/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Higit pang setting ng privacy"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Autofill, mga kontrol ng aktibidad, at higit pa"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Posibleng nanganganib ang account"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Nanganganib ang account"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-tr/strings.xml b/SafetyCenter/Resources/res/values-tr/strings.xml
index 12f81c3..111c35b 100644
--- a/SafetyCenter/Resources/res/values-tr/strings.xml
+++ b/SafetyCenter/Resources/res/values-tr/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Daha fazla gizlilik ayarı"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Otomatik doldurma, etkinlik kontrolleri ve daha fazlası"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Hesap risk altında olabilir"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Hesap risk altında"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-uk/strings.xml b/SafetyCenter/Resources/res/values-uk/strings.xml
index 6141794..b7f6342 100644
--- a/SafetyCenter/Resources/res/values-uk/strings.xml
+++ b/SafetyCenter/Resources/res/values-uk/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Інші налаштування конфіденційності"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Автозаповнення, відстеження дій тощо"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Можлива загроза обліковому запису"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Обліковий запис під загрозою"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-ur/strings.xml b/SafetyCenter/Resources/res/values-ur/strings.xml
index 69e3f2f..d66a27c 100644
--- a/SafetyCenter/Resources/res/values-ur/strings.xml
+++ b/SafetyCenter/Resources/res/values-ur/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"مزید رازداری کی ترتیبات"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"آٹو فل، سرگرمی کنٹرولز اور مزید"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"اکاؤنٹ خطرے میں ہو سکتا ہے"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"اکاؤنٹ خطرے میں ہے"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-uz/strings.xml b/SafetyCenter/Resources/res/values-uz/strings.xml
index c60a619..2d99d80 100644
--- a/SafetyCenter/Resources/res/values-uz/strings.xml
+++ b/SafetyCenter/Resources/res/values-uz/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Boshqa maxfiylik sozlamalari"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Avtomatik kiritish, harakatlarni kuzatish va boshqalar"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Hisob xavf ostida shekilli"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Hisob xavf ostida"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-vi/strings.xml b/SafetyCenter/Resources/res/values-vi/strings.xml
index 05e0339..1a1b22c 100644
--- a/SafetyCenter/Resources/res/values-vi/strings.xml
+++ b/SafetyCenter/Resources/res/values-vi/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Các chế độ cài đặt quyền riêng tư khác"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Các chế độ cài đặt tự động điền, kiểm soát hoạt động và nhiều chế độ khác"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"Tài khoản có thể gặp nguy hiểm"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"Tài khoản đang gặp nguy hiểm"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-zh-rCN/strings.xml b/SafetyCenter/Resources/res/values-zh-rCN/strings.xml
index 736ca8c..4348fb7 100644
--- a/SafetyCenter/Resources/res/values-zh-rCN/strings.xml
+++ b/SafetyCenter/Resources/res/values-zh-rCN/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"更多隐私设置"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"自动填充、活动控件等"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"帐号可能存在风险"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"帐号存在风险"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-zh-rHK/strings.xml b/SafetyCenter/Resources/res/values-zh-rHK/strings.xml
index ef552a0..0e18f30 100644
--- a/SafetyCenter/Resources/res/values-zh-rHK/strings.xml
+++ b/SafetyCenter/Resources/res/values-zh-rHK/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"更多私隱權設定"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"自動填入、活動控制項等等"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"帳戶可能面臨風險"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"裝置正面臨風險"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-zh-rTW/strings.xml b/SafetyCenter/Resources/res/values-zh-rTW/strings.xml
index 81cde82..5c44eb3 100644
--- a/SafetyCenter/Resources/res/values-zh-rTW/strings.xml
+++ b/SafetyCenter/Resources/res/values-zh-rTW/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"其他隱私權設定"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"自動填入、活動控制項等等"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"帳戶可能有風險"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"帳戶有風險"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values-zu/strings.xml b/SafetyCenter/Resources/res/values-zu/strings.xml
index 6d79eba..1362d09 100644
--- a/SafetyCenter/Resources/res/values-zu/strings.xml
+++ b/SafetyCenter/Resources/res/values-zu/strings.xml
@@ -43,6 +43,4 @@
     <string name="advanced_privacy_title" msgid="1117725225706176643">"Amasethingi obumfihlo engeziwe"</string>
     <string name="advanced_privacy_summary" msgid="2281203390575069543">"Ukugcwalisa okuzenzakalelayo, izilawuli zomsebenzi, kanye nokunye"</string>
     <string name="advanced_privacy_search_terms" msgid="5044404599789175222"></string>
-    <string name="overall_severity_level_account_recommendation_title" msgid="6524855037045765671">"I-akhawunti ingaba sengozini"</string>
-    <string name="overall_severity_level_critical_account_warning_title" msgid="3294187096461368277">"I-akhawunti isengcupheni"</string>
 </resources>
diff --git a/SafetyCenter/Resources/res/values/config.xml b/SafetyCenter/Resources/res/values/config.xml
index c0114e5..8ffc89c 100644
--- a/SafetyCenter/Resources/res/values/config.xml
+++ b/SafetyCenter/Resources/res/values/config.xml
@@ -20,4 +20,6 @@
     <string name="config_same_task_safety_source_ids" translatable="false">AndroidAccessibility,AndroidAdvancedPrivacy,AndroidAdvancedSecurity,AndroidBackgroundLocation,AndroidBiometrics,AndroidLockScreen,AndroidNotificationListener,AndroidPermissionAutoRevoke,AndroidPermissionManager,AndroidPermissionUsage,AndroidPrivacyControls,AndroidWorkPolicyInfo</string>
     <!-- Comma separated list of packages that are pregranted to enable a notification listener service. For T- platforms.-->
     <string name="config_NotificationListenerServicePregrants" translatable="false"></string>
-</resources>
\ No newline at end of file
+    <!-- Comma separated list of safety source IDs to add an Intent Extra confirming they should be displayed as if opened by a settings UI page. -->
+    <string name="config_useSettingsHomepageIntentExtra" translatable="false">AndroidAdvancedPrivacy,AndroidAdvancedSecurity</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values/strings.xml b/SafetyCenter/Resources/res/values/strings.xml
index 7585721..7621f46 100644
--- a/SafetyCenter/Resources/res/values/strings.xml
+++ b/SafetyCenter/Resources/res/values/strings.xml
@@ -51,10 +51,4 @@
     <string name="advanced_privacy_title" description="The title of the entry for advanced privacy settings">More privacy settings</string>
     <string name="advanced_privacy_summary" description="The summary of the entry for advanced privacy settings, which describes the page contents">Autofill, activity controls, and more</string>
     <string name="advanced_privacy_search_terms" description="Search keywords of the entry for advanced privacy settings"></string>
-
-    <!-- Status -->
-    <!-- Title for the overall Safety Center status when the user security and privacy signals could potentially put their account at risk [CHAR LIMIT=35] -->
-    <string name="overall_severity_level_account_recommendation_title">Account may be at risk</string>
-    <!-- Title for the overall Safety Center status when the user security and privacy signals are putting their account at risk [CHAR LIMIT=35] -->
-    <string name="overall_severity_level_critical_account_warning_title">Account is at risk</string>
 </resources>
diff --git a/SafetyCenter/Resources/shared_res/values-af/strings.xml b/SafetyCenter/Resources/shared_res/values-af/strings.xml
index 94eec6f..d35ccad 100644
--- a/SafetyCenter/Resources/shared_res/values-af/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-af/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Gaan instellingslys na"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Toestel is dalk in gevaar"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Toestel is in gevaar"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Jy is dalk in gevaar"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Jy is in gevaar"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Jou data is dalk in gevaar"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Jou data is in gevaar"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Wagwoorde is gekompromitteer (oud)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Wagwoorde is gekompromitteer (nuut)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Jy is dalk in gevaar"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Jy is in gevaar"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potensiële risiko’s gevind"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risiko’s gevind"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Rekening is dalk in gevaar"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Rekening is in gevaar"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# opletberig}other{# opletberigte}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Kon nie bladsy oopmaak nie"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Kon nie opletberig afhandel nie"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-am/strings.xml b/SafetyCenter/Resources/shared_res/values-am/strings.xml
index e406553..028d60f 100644
--- a/SafetyCenter/Resources/shared_res/values-am/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-am/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"የቅንብሮች ዝርዝርን ይፈትሹ"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"መሣሪያ አደጋ ላይ ሊሆን ይችላል"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"መሣሪያ አደጋ ላይ ነው"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"ተጋላጭ ሊሆኑ ይችላሉ"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"ተጋላጭ ነዎት"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ውሂብዎ አደጋ ላይ ሊሆን ይችላል"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"ውሂብዎ አደጋ ላይ ነው"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"የይለፍ ቃላት ተጠልፈዋል (የድሮ)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"የይለፍ ቃላት ተጠልፈዋል (አዲስ)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"አደጋ ላይ ሊሆኑ ይችላሉ"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"አደጋ ላይ ነዎት"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"ሊከሰቱ የሚችሉ አደጋዎች ተገኝተዋል"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"አደጋዎች ተገኝተዋል"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"መለያ አደጋ ላይ ሊሆን ይችላል"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"መለያ አደጋ ላይ ነው"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ማንቂያ}one{# ማንቂያ}other{# ማንቂያዎች}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"ገጹን መከፈት አልተቻለም"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"ማንቂያን መፍታት አልተቻለም"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ar/strings.xml b/SafetyCenter/Resources/shared_res/values-ar/strings.xml
index 98f86cd..45f632f 100644
--- a/SafetyCenter/Resources/shared_res/values-ar/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ar/strings.xml
@@ -25,8 +25,22 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"الاطّلاع على قائمة الإعدادات"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"الجهاز قد يكون معرّضًا للخطر"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"الجهاز معرّض للخطر"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"قد تكون معرّضًا للخطر"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"أنت معرّض للخطر"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"قد تكون بياناتك معرّضة للخطر"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"بياناتك معرّضة للخطر"</string>
+    <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) -->
+    <skip />
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"قد يكون الحساب معرّضًا للخطر"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"الحساب معرّض للخطر"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{تنبيه واحد (#)}zero{# تنبيه}two{تنبيهان}few{# تنبيهات}many{# تنبيهًا}other{# تنبيه}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"تعذَّر فتح الصفحة"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"تعذَّر التعامل بشكل نهائي مع التنبيه"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-as/strings.xml b/SafetyCenter/Resources/shared_res/values-as/strings.xml
index 10f6c32..b1d0065 100644
--- a/SafetyCenter/Resources/shared_res/values-as/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-as/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ছেটিঙৰ সূচী পৰীক্ষা কৰক"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ডিভাইচটোৰ বিপদাশংকা থাকিব পাৰে"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ডিভাইচটো অসুৰক্ষিত অৱস্থাত আছে"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"আপুনি আশংকাত থাকিব পাৰে"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"আপুনি আশংকাত আছে"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"আপোনাৰ ডেটাখিনিৰ বিপদাশংকা থাকিব পাৰে"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"আপোনাৰ ডেটাখিনিৰ বিপদাশংকা আছে"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"পাছৱৰ্ড হেক কৰা হৈছে (পুৰণি)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"পাছৱৰ্ড হেক কৰা হৈছে (নতুন)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"একাউণ্টত বিপদাশংকা থাকিব পাৰে"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"আপোনাৰ একাউণ্টত বিপদাশংকা আছে"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"সম্ভাব্য বিপদাশংকা পোৱা গৈছে"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"বিপদাশংকা পোৱা গৈছে"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"একাউণ্টটোত বিপদাশংকা থাকিব পাৰে"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"একাউণ্টৰ বিপদাশংকা আছে"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# টা সতৰ্কবাৰ্তা}one{# সতৰ্কবাৰ্তা}other{# সতৰ্কবাৰ্তা}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"পৃষ্ঠাখন খুলিব পৰা নগ’ল"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"সতৰ্কবাৰ্তা সমাধান কৰিব পৰা নগ’ল"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-az/strings.xml b/SafetyCenter/Resources/shared_res/values-az/strings.xml
index b2126f9..96510a6 100644
--- a/SafetyCenter/Resources/shared_res/values-az/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-az/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Ayarlar siyahısını yoxlayın"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Cihaz risk altında ola bilər"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Cihaz risk altındadır"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Risk altında ola bilərsiniz"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Risk altındasınız"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Datanız risk altında ola bilər"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Datanız risk altındadır"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Parollar oğurlanıb (köhnə)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Parollar oğurlanıb (yeni)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Risk altında ola bilərsiniz"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Risk altındasınız"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potensial risklər tapıldı"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risklər tapıldı"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Hesab risk altında ola bilər"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Hesab risk altındadır"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# siqnal}other{# siqnal}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Səhifəni açmaq mümkün olmadı"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Siqnalı həll etmək mümkün olmadı"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-b+sr+Latn/strings.xml b/SafetyCenter/Resources/shared_res/values-b+sr+Latn/strings.xml
index 1c0bdc3..30b6505 100644
--- a/SafetyCenter/Resources/shared_res/values-b+sr+Latn/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-b+sr+Latn/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Proverite listu podešavanja"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Uređaj je možda ugrožen"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Uređaj je ugrožen"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Možda ste ugroženi"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Ugroženi ste"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Podaci su možda ugroženi"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Podaci su ugroženi"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Lozinke su ugrožene (stare)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Lozinke su ugrožene (nove)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Možda ste ugroženi"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Ugroženi ste"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Pronađeni su potencijalni rizici"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Pronađeni su rizici"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Nalog je možda ugrožen"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Nalog je ugrožen"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# obaveštenje}one{# obaveštenje}few{# obaveštenja}other{# obaveštenja}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Otvaranje stranice nije uspelo"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Rešavanje obaveštenja nije uspelo"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-be/strings.xml b/SafetyCenter/Resources/shared_res/values-be/strings.xml
index e604b57..e76aefa 100644
--- a/SafetyCenter/Resources/shared_res/values-be/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-be/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Праверыць спіс налад"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Прылада можа быць у небяспецы"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Прылада ў небяспецы"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Вы можаце быць у небяспецы"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Вы ў небяспецы"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Вашы даныя могуць быць у небяспецы"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Вашы даныя ў небяспецы"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Раскрытыя паролі (старыя)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Раскрытыя паролі (новыя)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Вы можаце быць у небяспецы"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Вы ў небяспецы"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Выяўлена патэнцыяльная небяспека"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Выяўлена небяспека"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Уліковы запіс можа быць у небяспецы"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Уліковы запіс у небяспецы"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# абвестка}one{# абвестка}few{# абвесткі}many{# абвестак}other{# абвесткі}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Не ўдалося адкрыць старонку"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Не ўдалося вырашыць праблему"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-bg/strings.xml b/SafetyCenter/Resources/shared_res/values-bg/strings.xml
index fdc6e80..ad13d48 100644
--- a/SafetyCenter/Resources/shared_res/values-bg/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-bg/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Проверка на списъка с настройки"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"У-вото може да е изложено на риск"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Устройството е изложено на риск"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Има потенциален риск за вас"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Има риск за вас"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Данните може да са изложени на риск"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Данните ви са изложени на риск"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Компрометирани пароли (стари)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Компрометирани пароли (нови)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Има потенциален риск за вас"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Има риск за вас"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Открити са потенциални рискове"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Открити са рискове"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Профилът може да е изложен на риск"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Профилът е изложен на риск"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# сигнал}other{# сигнала}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Страницата не се отвори"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Сигналът не се отстрани"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-bn/strings.xml b/SafetyCenter/Resources/shared_res/values-bn/strings.xml
index 2ee0bfc..7646005 100644
--- a/SafetyCenter/Resources/shared_res/values-bn/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-bn/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"সেটিংস তালিকা চেক করুন"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ডিভাইসের নিরাপত্তা বিপন্ন হতে পারে"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ডিভাইসের নিরাপত্তা বিপন্ন"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"আপনার নিরাপত্তা বিপন্ন হতে পারে"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"আপনার নিরাপত্তা বিপন্ন"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"আপনার ডেটার হয়ত ক্ষতি হতে পারে"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"আপনার ডেটার ক্ষতি হতে পারে"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"পাসওয়ার্ড চুরি হয়ে গেছে (পুরনো)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"পাসওয়ার্ড চুরি হয়ে গেছে (নতুন)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"আপনার নিরাপত্তা বিপন্ন হতে পারে"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"আপনার নিরাপত্তা বিপন্ন"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"সম্ভাব্য ঝুঁকি খুঁজে পাওয়া গেছে"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"ঝুঁকির বিষয়টি খুঁজে পাওয়া গেছে"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"অ্যাকাউন্টের হয়ত ক্ষতি হতে পারে"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"অ্যাকাউন্টের ক্ষতি হতে পারে"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{#টি সতর্কতা}one{#টি সতর্কতা}other{#টি সতর্কতা}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"পৃষ্ঠা খোলা যায়নি"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"সতর্কতার সমাধান করা যায়নি"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-bs/strings.xml b/SafetyCenter/Resources/shared_res/values-bs/strings.xml
index 6c32a9d..62c542e 100644
--- a/SafetyCenter/Resources/shared_res/values-bs/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-bs/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Provjerite listu postavki"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Uređaj bi mogao biti izložen riziku"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Uređaj je izložen riziku"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Moguće je da ste izloženi riziku"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Izloženi ste riziku"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Vaši podaci bi mogli biti izloženi riziku"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Vaši podaci su izloženi riziku"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Lozinke su ugrožene (stare)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Lozinke su ugrožene (nove)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Možda ste izloženi riziku"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Izloženi ste riziku"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Pronađeni su potencijalni rizici"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Pronađeni su rizici"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Račun bi mogao biti izložen riziku"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Račun je izložen riziku"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# upozorenje}one{# upozorenje}few{# upozorenja}other{# upozorenja}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Otvaranje stranice nije uspjelo"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Rješavanje upozorenja nije uspjelo"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ca/strings.xml b/SafetyCenter/Resources/shared_res/values-ca/strings.xml
index abfd5ca..c674147 100644
--- a/SafetyCenter/Resources/shared_res/values-ca/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ca/strings.xml
@@ -25,8 +25,22 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Comprova la llista de configuració"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"El dispositiu pot estar en perill"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"El dispositiu està en perill"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Pot ser que estiguis en perill"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Estàs en perill"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Les dades poden estar en perill"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Les dades estan en perill"</string>
+    <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) -->
+    <skip />
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"El compte pot estar en perill"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Aquest compte està en perill"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerta}other{# alertes}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"No s\'ha pogut obrir la pàgina"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"No s\'ha pogut resoldre l\'alerta"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-cs/strings.xml b/SafetyCenter/Resources/shared_res/values-cs/strings.xml
index d912703..6030875 100644
--- a/SafetyCenter/Resources/shared_res/values-cs/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-cs/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Zkontrolujte seznam nastavení"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Zařízení může být ohroženo"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Zařízení je ohroženo"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Můžete být ohroženi"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Jste v ohrožení"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Data mohou být ohrožena"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Data jsou ohrožena"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Prolomená hesla (staré)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Prolomená hesla (nové)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Můžete být ohroženi"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Jste v ohrožení"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Byla nalezena možná rizika"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Byla nalezena rizika"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Účet může být ohrožen"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Účet je ohrožen"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# upozornění}few{# upozornění}many{# upozornění}other{# upozornění}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Stránku nelze otevřít"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Upozornění se nepodařilo vyřešit"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-da/strings.xml b/SafetyCenter/Resources/shared_res/values-da/strings.xml
index 58cf3ae..634d5d4 100644
--- a/SafetyCenter/Resources/shared_res/values-da/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-da/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Tjek listen over indstillinger"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Enheden kan være sårbar"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Enheden er sårbar"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Du er muligvis i fare"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Du er i fare"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Dine data kan være sårbare"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Dine data er sårbare"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Kompromitterede adgangskoder (gamle)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Kompromitterede adgangskoder (nye)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Din konto er muligvis sårbar"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Din konto er sårbar"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Der blev fundet potentielle risici"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Der blev fundet risici"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Kontoen kan være sårbar"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Kontoen er sårbar"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# underretning}one{# underretning}other{# underretninger}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Siden kunne ikke åbnes"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Underretningen kunne ikke behandles"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-de/strings.xml b/SafetyCenter/Resources/shared_res/values-de/strings.xml
index 2047e88..a6872dc 100644
--- a/SafetyCenter/Resources/shared_res/values-de/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-de/strings.xml
@@ -25,9 +25,17 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Liste der Einstellungen prüfen"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Gerät ist eventuell gefährdet"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Gerät ist gefährdet"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Du bist möglicherweise gefährdet"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Du bist gefährdet"</string>
-    <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# Benachrichtigung}other{# Benachrichtigungen}}"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Daten eventuell gefährdet"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Daten gefährdet"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"(Alte) Passwörter gefährdet"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"(Neue) Passwörter gefährdet"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Du bist möglicherweise gefährdet"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Du bist gefährdet"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Mögliche Sicherheitsrisiken gefunden"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Sicherheitsrisiken gefunden"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Konto eventuell gefährdet"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Konto gefährdet"</string>
+    <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# Warnung}other{# Warnungen}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Seite konnte nicht geöffnet werden"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Ursache konnte nicht behoben werden"</string>
     <string name="refresh_timeout" msgid="251734999692581852">"Einstellungen konnten nicht aktualisiert werden"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-el/strings.xml b/SafetyCenter/Resources/shared_res/values-el/strings.xml
index 6abf419..6c98e08 100644
--- a/SafetyCenter/Resources/shared_res/values-el/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-el/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Έλεγχος λίστας ρυθμίσεων"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Η συσκευή μπορεί να κινδυνεύει"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Η συσκευή κινδυνεύει"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Ενδέχεται να βρίσκεστε σε κίνδυνο"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Βρίσκεστε σε κίνδυνο"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Τα δεδομένα σας μπορεί να κινδυν."</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Τα δεδομένα σας κινδυνεύουν"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Παραβιάστηκαν κωδ. πρόσβ. (παλιοί)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Παραβιάστηκαν κωδ. πρόσβασης (νέοι)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Ενδέχεται να βρίσκεστε σε κίνδυνο"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Βρίσκεστε σε κίνδυνο"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Εντοπίστηκαν πιθανοί κίνδυνοι"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Εντοπίστηκαν κίνδυνοι"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Ο λογαριασμός μπορεί να κινδυνεύει"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Ο λογαριασμός βρίσκεται σε κίνδυνο"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ειδοποίηση}other{# ειδοποιήσεις}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Δεν ήταν δυνατό το άνοιγμα της σελίδας"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Δεν ήταν δυνατή η επίλυση της ειδοποίησης"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-en-rAU/strings.xml b/SafetyCenter/Resources/shared_res/values-en-rAU/strings.xml
index 63a8ad8..26967f2 100644
--- a/SafetyCenter/Resources/shared_res/values-en-rAU/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-en-rAU/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Check settings list"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Device may be at risk"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Device is at risk"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"You may be at risk"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"You are at risk"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Your data may be at risk"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Your data is at risk"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Passwords compromised (old)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Passwords compromised (new)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"You may be at risk"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"You are at risk"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potential risks found"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risks found"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Account may be at risk"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Account is at risk"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alert}other{# alerts}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Couldn\'t open page"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Couldn\'t resolve alert"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-en-rCA/strings.xml b/SafetyCenter/Resources/shared_res/values-en-rCA/strings.xml
index 63a8ad8..c26f0d4 100644
--- a/SafetyCenter/Resources/shared_res/values-en-rCA/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-en-rCA/strings.xml
@@ -19,19 +19,27 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="scanning_title" msgid="5424849039854311398">"Scanning"</string>
     <string name="loading_summary" msgid="3740846439782713910">"Checking device settings…"</string>
-    <string name="overall_severity_level_ok_title" msgid="2041250138727564565">"Looks fine"</string>
+    <string name="overall_severity_level_ok_title" msgid="2041250138727564565">"Looks good"</string>
     <string name="overall_severity_level_ok_summary" msgid="7219520381757200598">"No problems found"</string>
     <string name="overall_severity_level_ok_review_title" msgid="1494321117696765360">"Review settings"</string>
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Check settings list"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Device may be at risk"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Device is at risk"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"You may be at risk"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"You are at risk"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Your data may be at risk"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Your data is at risk"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Passwords compromised (old)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Passwords compromised (new)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"You may be at risk"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"You are at risk"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potential risks found"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risks found"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Account may be at risk"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Account is at risk"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alert}other{# alerts}}"</string>
-    <string name="redirecting_error" msgid="8146983632878233202">"Couldn\'t open page"</string>
-    <string name="resolving_action_error" msgid="371968886143262375">"Couldn\'t resolve alert"</string>
-    <string name="refresh_timeout" msgid="251734999692581852">"Couldn\'t refresh settings"</string>
-    <string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Couldn\'t check setting}other{Couldn\'t check settings}}"</string>
+    <string name="redirecting_error" msgid="8146983632878233202">"Couldnt open page"</string>
+    <string name="resolving_action_error" msgid="371968886143262375">"Couldnt resolve alert"</string>
+    <string name="refresh_timeout" msgid="251734999692581852">"Couldnt refresh settings"</string>
+    <string name="refresh_error" msgid="656062128422446177">"{count,plural, =1{Couldnt check setting}other{Couldnt check settings}}"</string>
     <string name="work_profile_paused" msgid="7037400224040869079">"Work profile is paused"</string>
     <string name="group_unknown_summary" msgid="6951386960814105641">"No info yet"</string>
 </resources>
diff --git a/SafetyCenter/Resources/shared_res/values-en-rGB/strings.xml b/SafetyCenter/Resources/shared_res/values-en-rGB/strings.xml
index 63a8ad8..26967f2 100644
--- a/SafetyCenter/Resources/shared_res/values-en-rGB/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-en-rGB/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Check settings list"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Device may be at risk"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Device is at risk"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"You may be at risk"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"You are at risk"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Your data may be at risk"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Your data is at risk"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Passwords compromised (old)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Passwords compromised (new)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"You may be at risk"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"You are at risk"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potential risks found"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risks found"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Account may be at risk"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Account is at risk"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alert}other{# alerts}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Couldn\'t open page"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Couldn\'t resolve alert"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-en-rIN/strings.xml b/SafetyCenter/Resources/shared_res/values-en-rIN/strings.xml
index 63a8ad8..26967f2 100644
--- a/SafetyCenter/Resources/shared_res/values-en-rIN/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-en-rIN/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Check settings list"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Device may be at risk"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Device is at risk"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"You may be at risk"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"You are at risk"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Your data may be at risk"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Your data is at risk"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Passwords compromised (old)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Passwords compromised (new)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"You may be at risk"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"You are at risk"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potential risks found"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risks found"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Account may be at risk"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Account is at risk"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alert}other{# alerts}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Couldn\'t open page"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Couldn\'t resolve alert"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-en-rXC/strings.xml b/SafetyCenter/Resources/shared_res/values-en-rXC/strings.xml
index 831bcaf..ba21dba 100644
--- a/SafetyCenter/Resources/shared_res/values-en-rXC/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-en-rXC/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎Check settings list‎‏‎‎‏‎"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‏‏‎‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‎‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‎Device may be at risk‎‏‎‎‏‎"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‏‏‎‎‏‎‏‏‎‎‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‎‎Device is at risk‎‏‎‎‏‎"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‏‎‎‏‎‎‏‏‏‎‎‏‏‏‎‏‎‎‏‏‎‏‏‏‏‎You may be at risk‎‏‎‎‏‎"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‎‎‎You are at risk‎‏‎‎‏‎"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‎‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‎‏‏‏‎‎‏‎‏‎‏‎‏‏‎‏‎‎‎‎‎‏‎‏‎‎‎‎‎‏‏‎‎Your data may be at risk‎‏‎‎‏‎"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‎‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‎‏‏‎‏‎‎‎‏‎‏‎‏‎‎‎‏‎‎‎‏‎‎‏‏‏‎‏‏‎‏‏‎‎‎Your data is at risk‎‏‎‎‏‎"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‎‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‎‎‏‏‏‏‎‎‏‎‎‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‏‏‎Passwords compromised (old)‎‏‎‎‏‎"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‎‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‏‏‎‏‏‎‏‎‎‎‎‏‎‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎Passwords compromised (new)‎‏‎‎‏‎"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‎‏‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‎‏‎‎‎‎‎‎‎‎‎‏‎‎‎‏‏‎You may be at risk‎‏‎‎‏‎"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‎‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‎‎‎‏‏‏‏‏‎‎You are at risk‎‏‎‎‏‎"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‏‏‏‏‎‎‎‎‏‏‏‎‎‎‎‎‏‏‏‎‎‏‎‏‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‎‏‏‎‎‏‏‎‏‎‎‏‎Potential risks found‎‏‎‎‏‎"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‏‎‎‏‎‏‎‏‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‎‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎Risks found‎‏‎‎‏‎"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‎‎‎‎‎‏‎‏‏‎‎‎‎‏‎‎‏‎‎Account may be at risk‎‏‎‎‏‎"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‏‏‎‎‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‏‎‎‎‎‏‎‎‎‏‎‏‏‎‎‏‎‎‎‏‎Account is at risk‎‏‎‎‏‎"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‏‎‏‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‎‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎# alert‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‏‎‏‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‎‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎# alerts‎‏‎‎‏‎}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‎‎‏‏‎‎‏‏‏‎‎‏‎‎Couldnt open page‎‏‎‎‏‎"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‎‎‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎Couldnt resolve alert‎‏‎‎‏‎"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-es-rUS/strings.xml b/SafetyCenter/Resources/shared_res/values-es-rUS/strings.xml
index 29b2808..2440fa8 100644
--- a/SafetyCenter/Resources/shared_res/values-es-rUS/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-es-rUS/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Verifica la lista de configuración"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"El dispositivo podría estar en riesgo"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"El dispositivo está en riesgo"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Es posible que estés en peligro"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Estás en peligro"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Tus datos podrían estar en riesgo"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Tus datos están en riesgo"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Contraseñas comprometidas (viejas)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Contraseñas comprometidas (nuevas)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Es posible que estés en peligro"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Estás en peligro"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Se detectaron riesgos potenciales"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Se detectaron riesgos"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"La cuenta podría estar en riesgo"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"La cuenta está en riesgo"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerta}many{# alertas}other{# alertas}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"No se pudo abrir la página"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"No se pudo resolver la alerta"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-es/strings.xml b/SafetyCenter/Resources/shared_res/values-es/strings.xml
index 5f1461e..78fc554 100644
--- a/SafetyCenter/Resources/shared_res/values-es/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-es/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Comprueba la lista de ajustes"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"El dispositivo puede estar en riesgo"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"El dispositivo está en riesgo"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Puedes estar en riesgo"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Estás en riesgo"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Tus datos pueden estar en riesgo"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Tus datos están en riesgo"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Contraseñas vulneradas (antiguas)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Contraseñas vulneradas (nuevas)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Puedes estar en riesgo"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Estás en riesgo"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Posibles riesgos detectados"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Riesgos detectados"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"La cuenta puede estar en riesgo"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"La cuenta está en riesgo"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerta}many{# alertas}other{# alertas}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"No se ha podido abrir la página"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"No se ha podido resolver la alerta"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-et/strings.xml b/SafetyCenter/Resources/shared_res/values-et/strings.xml
index fac9f7d..f8eec7f 100644
--- a/SafetyCenter/Resources/shared_res/values-et/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-et/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Kontrollige seadete loendit"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Seade võib olla ohus"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Seade on ohus"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Võite olla ohus"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Olete ohus"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Teie andmed võivad ohus olla"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Teie andmed on ohus"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Paroolid on ohus (vana)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Paroolid on ohus (uus)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Võite olla ohus"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Olete ohus"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Leiti võimalikud ohud"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Leiti ohud"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Konto võib olla ohus"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Konto on ohus"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# hoiatus}other{# hoiatust}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Lehte ei saanud avada"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Hoiatusega seotud probleemi ei saanud lahendada"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-eu/strings.xml b/SafetyCenter/Resources/shared_res/values-eu/strings.xml
index 6cdfb2d..54d7f98 100644
--- a/SafetyCenter/Resources/shared_res/values-eu/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-eu/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Egiaztatu ezarpenen zerrenda"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Baliteke gailua arriskuan egotea"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Gailua arriskuan dago"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Baliteke arriskuan egotea"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Arriskuan zaude"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Baliteke datuak arriskuan egotea"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Datuak arriskuan daude"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Pasahitz zaharrak arriskuan daude"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Pasahitz berriak arriskuan daude"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Baliteke arriskuan egotea"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Arriskuan zaude"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Balizko arriskuak aurkitu dira"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Arriskuak aurkitu dira"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Baliteke kontua arriskuan egotea"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Kontua arriskuan dago"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerta}other{# alerta}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Ezin da ireki orria"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Ezin izan da ebatzi alerta"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-fa/strings.xml b/SafetyCenter/Resources/shared_res/values-fa/strings.xml
index bf478ae..35babbd 100644
--- a/SafetyCenter/Resources/shared_res/values-fa/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-fa/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"بررسی فهرست تنظیمات"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"دستگاه ممکن است درمعرض خطر باشد"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"دستگاه درمعرض خطر است"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"ممکن است درمعرض خطر باشید"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"درمعرض خطر هستید"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"شاید داده‌هایتان درمعرض خطر باشد"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"داده‌هایتان درمعرض خطر است"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"گذرواژه‌ها لو رفته است (قدیمی)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"گذرواژه‌ها لو رفته است (جدید)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"ممکن است درمعرض خطر باشید"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"درمعرض خطر هستید"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"خطرات احتمالی پیدا شده است"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"خطراتی پیدا شده است"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"ممکن است حساب درمعرض خطر باشد"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"حساب درمعرض خطر است"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# هشدار}one{# هشدار}other{# هشدار}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"صفحه باز نشد"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"هشدار رفع نشد"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-fi/strings.xml b/SafetyCenter/Resources/shared_res/values-fi/strings.xml
index 979b478..a2dea42 100644
--- a/SafetyCenter/Resources/shared_res/values-fi/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-fi/strings.xml
@@ -25,8 +25,22 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Tarkista asetuslista"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Laite saattaa olla vaarantunut"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Laite on vaarantunut"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Turvallisuutesi on voinut vaarantua"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Turvallisuutesi on vaarantanut"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Data voi olla vaarantunut"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Data on vaarantunut"</string>
+    <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) -->
+    <skip />
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Tili voi olla vaarantunut"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Tili on vaarantunut"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# hälytys}other{# hälytystä}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Sivun avaaminen epäonnistui"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Hälytyksen ratkaiseminen epäonnistui"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-fr-rCA/strings.xml b/SafetyCenter/Resources/shared_res/values-fr-rCA/strings.xml
index 69d3dda..64a6901 100644
--- a/SafetyCenter/Resources/shared_res/values-fr-rCA/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-fr-rCA/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Vérifiez la liste des paramètres"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"L\'appareil pourrait être en danger"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"L\'appareil est en danger"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Vous pourriez être en danger"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Vous êtes en danger"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Vos données pourraient être en danger"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Vos données sont en danger"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Mots de passe compromis (ancien)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Mots de passe compromis (nouveau)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Vous pourriez être en danger"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Vous êtes en danger"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Dangers potentiels trouvés"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Dangers trouvés"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Le compte pourrait être en danger"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Le compte est en danger"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerte}one{# alerte}many{# alertes}other{# alertes}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Impossible d\'ouvrir la page"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Impossible de résoudre l\'alerte"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-fr/strings.xml b/SafetyCenter/Resources/shared_res/values-fr/strings.xml
index 3bbe6ca..e1e716f 100644
--- a/SafetyCenter/Resources/shared_res/values-fr/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-fr/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Vérifier la liste des paramètres"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Risque potentiel sur l\'appareil"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Risque sur l\'appareil"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Risque potentiel"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Risque de sécurité"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Risque potentiel sur vos données"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Risque sur vos données"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Mots de passe compromis (anciens)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Mots de passe compromis (nouveaux)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Risque potentiel"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Risque de sécurité"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Risques potentiels détectés"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risques détectés"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Risque potentiel sur le compte"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Risque sur le compte"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerte}one{# alerte}many{# alertes}other{# alertes}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Impossible d\'accéder à la page"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Impossible de résoudre l\'alerte"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-gl/strings.xml b/SafetyCenter/Resources/shared_res/values-gl/strings.xml
index 2d2b1e3..fd2df84 100644
--- a/SafetyCenter/Resources/shared_res/values-gl/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-gl/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Revisa a lista de opcións de configuración"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"O dispositivo pode estar en risco"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"O dispositivo está en risco"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Pode que esteas en risco"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Estás en risco"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Os teus datos poden estar en risco"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Os teus datos están en risco"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Contrasinais vulnerados (antigos)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Contrasinais vulnerados (novos)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Pode que a túa conta estea en risco"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"A túa conta está en risco"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Atopáronse posibles riscos"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Atopáronse riscos"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"A conta pode estar en risco"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"A conta está en risco"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerta}other{# alertas}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Non se puido abrir a páxina"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Non se puido resolver a alerta"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-gu/strings.xml b/SafetyCenter/Resources/shared_res/values-gu/strings.xml
index 5e39e09..85afd57 100644
--- a/SafetyCenter/Resources/shared_res/values-gu/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-gu/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"સેટિંગની સૂચિ ચેક કરો"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ડિવાઇસ કદાચ જોખમમાં હોઈ શકે"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ડિવાઇસ જોખમમાં છે"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"તમારી સુરક્ષા જોખમમાં હોઈ શકે છે"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"તમારી સુરક્ષા જોખમમાં છે"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"તમારો ડેટા જોખમમાં હોઈ શકે છે"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"તમારો ડેટા જોખમમાં છે"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"પાસવર્ડ સાથે ચેડાં થયા (જૂના)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"પાસવર્ડ સાથે ચેડાં થયા (નવા)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"તમારી સુરક્ષા જોખમમાં હોઈ શકે છે"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"તમારી સુરક્ષા જોખમમાં છે"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"સંભવિત જોખમો મળ્યા"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"જોખમો મળ્યા"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"એકાઉન્ટ જોખમમાં હોઈ શકે છે"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"એકાઉન્ટ જોખમમાં છે"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# અલર્ટ}one{# અલર્ટ}other{# અલર્ટ}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"પેજ ખોલી શક્યા નથી"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"અલર્ટનું નિરાકરણ લાવી શક્યા નથી"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-hi/strings.xml b/SafetyCenter/Resources/shared_res/values-hi/strings.xml
index a67e717..e69dfc0 100644
--- a/SafetyCenter/Resources/shared_res/values-hi/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-hi/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"सेटिंग की सूची देखें"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"डिवाइस की सुरक्षा शायद खतरे में है"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"डिवाइस की सुरक्षा खतरे में है"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"शायद आपकी सुरक्षा खतरे में है"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"आपकी सुरक्षा खतरे में है"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"डेटा की सुरक्षा खतरे में हो सकती है"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"डेटा की सुरक्षा खतरे में है"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"पासवर्ड हैक हो गया (पुराना)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"पासवर्ड हैक हो गया (नया)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"शायद आपकी सुरक्षा खतरे में है"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"आपकी सुरक्षा खतरे में है"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"संभावित खतरे का पता चला"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"खतरे का पता चला"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"खाते की सुरक्षा खतरे में हो सकती है"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"खाते की सुरक्षा खतरे में है"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# चेतावनी}one{# चेतावनी}other{# चेतावनियां}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"पेज को खोला नहीं जा सका"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"चेतावनी में बताई गई समस्या को ठीक नहीं किया जा सका"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-hr/strings.xml b/SafetyCenter/Resources/shared_res/values-hr/strings.xml
index aa64abb..e345b9b 100644
--- a/SafetyCenter/Resources/shared_res/values-hr/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-hr/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Provjera popisa postavki"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Uređaj je možda ugrožen"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Uređaj je ugrožen"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Vaša je sigurnost možda ugrožena"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Vaša je sigurnost ugrožena"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Podaci mogu biti ugroženi"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Podaci su ugroženi"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Zaporke su ugrožene (staro)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Zaporke su ugrožene (novo)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Vaša je sigurnost možda ugrožena"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Vaša je sigurnost ugrožena"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Pronađeni su potencijalni rizici"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Pronađeni su rizici"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Račun je možda ugrožen"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Račun je ugrožen"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# upozorenje}one{# upozorenje}few{# upozorenja}other{# upozorenja}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Otvaranje stranice nije uspjelo"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Razrješavanje upozorenja nije uspjelo"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-hu/strings.xml b/SafetyCenter/Resources/shared_res/values-hu/strings.xml
index 95b5384..e3e2640 100644
--- a/SafetyCenter/Resources/shared_res/values-hu/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-hu/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Ellenőrizze a beállításlistát"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Az eszközhasználat kockázatos lehet"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Az eszköz használata kockázatos"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Veszélyben lehet"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Veszélyben van"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Adatai veszélyben lehetnek"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Adatai veszélyben vannak"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Feltört jelszavak (régi)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Feltört jelszavak (új)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Veszélyben lehet"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Veszélyben van"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Lehetséges veszélyeket észleltünk"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Veszélyeket észleltünk"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Fiókja veszélyben lehet"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Fiókja veszélyben van"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# értesítés}other{# értesítés}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Nem sikerült megnyitni az oldalt"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Nem sikerült feloldani a figyelmeztetést"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-hy/strings.xml b/SafetyCenter/Resources/shared_res/values-hy/strings.xml
index 9f0bb6b..3f4f9e3 100644
--- a/SafetyCenter/Resources/shared_res/values-hy/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-hy/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Ստուգեք կարգավորումների ցանկը"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Սարքը կարող է վտանգված լինել"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Սարքը վտանգված է"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Դուք կարող եք վտանգի տակ լինել"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Դուք վտանգի տակ եք"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Տվյալները կարող են վտանգված լինել"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Ձեր տվյալները վտանգված են"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Հին գաղտնաբառերը կոտրված են"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Նոր գաղտնաբառերը կոտրված են"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Դուք կարող եք վտանգված լինել"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Դուք վտանգված եք"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Հնարավոր ռիսկեր են հայտնաբերվել"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Ռիսկեր են հայտնաբերվել"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Հաշիվը կարող է վտանգված լինել"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Հաշիվը վտանգված է"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ծանուցում}one{# ծանուցում}other{# ծանուցում}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Չհաջողվեց բացել էջը"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Չհաջողվեց լուծել ծանուցումը"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-in/strings.xml b/SafetyCenter/Resources/shared_res/values-in/strings.xml
index c5fc1fd..8c0b3a2 100644
--- a/SafetyCenter/Resources/shared_res/values-in/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-in/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Periksa daftar setelan"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Perangkat mungkin berisiko"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Perangkat berisiko"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Anda mungkin berisiko"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Anda berisiko"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Data Anda mungkin berisiko"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Data Anda berisiko"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Sandi disusupi (lama)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Sandi disusupi (baru)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Anda mungkin berisiko"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Anda berisiko"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potensi risiko ditemukan"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risiko ditemukan"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Akun mungkin berisiko"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Akun berisiko"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# peringatan}other{# peringatan}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Tidak dapat membuka halaman"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Tidak dapat menyelesaikan peringatan"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-is/strings.xml b/SafetyCenter/Resources/shared_res/values-is/strings.xml
index 7dcb418..b003327 100644
--- a/SafetyCenter/Resources/shared_res/values-is/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-is/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Athuga lista yfir stillingar"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Tækið er hugsanlega í hættu"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Tækið er í hættu"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Þú gætir verið í hættu"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Þú ert í hættu"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Gögnin þín gætu verið í hættu"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Gögnin þín eru í hættu"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Aðgangsorð í hættu (gömul)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Aðgangsorð í hættu (ný)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Þú gætir verið í hættu"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Þú ert í hættu"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Hugsanleg hætta greindist"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Hætta greindist"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Reikningurinn er hugsanlega í hættu"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Reikningurinn er í hættu"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# viðvörun}one{# viðvörun}other{# viðvaranir}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Ekki tókst að opna síðuna"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Ekki tókst að leysa úr viðvöruninni"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-it/strings.xml b/SafetyCenter/Resources/shared_res/values-it/strings.xml
index a040438..7535a16 100644
--- a/SafetyCenter/Resources/shared_res/values-it/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-it/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Controlla l\'elenco di impostazioni"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Il dispositivo potrebbe essere a rischio"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Il dispositivo è a rischio"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Potresti essere a rischio"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Sei a rischio"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"I dati potrebbero essere a rischio"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"I dati sono a rischio"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Password compromesse (precedenti)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Password compromesse (nuove)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Potresti essere a rischio"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Sei a rischio"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potenziali rischi rilevati"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Rischi rilevati"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"L\'account potrebbe essere a rischio"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"L\'account è a rischio"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# avviso}many{# avvisi}other{# avvisi}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Impossibile aprire la pagina"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Impossibile risolvere l\'avviso"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-iw/strings.xml b/SafetyCenter/Resources/shared_res/values-iw/strings.xml
index 2491884..e5f2c15 100644
--- a/SafetyCenter/Resources/shared_res/values-iw/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-iw/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"בדיקה של רשימת ההגדרות"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"המכשיר עלול להיות בסיכון"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"המכשיר בסיכון"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"ייתכן שקיים סיכון"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"קיים סיכון"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ייתכן שהנתונים שלך בסיכון"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"הנתונים שלך בסיכון"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"סיסמאות בסכנת חשיפה (ישנות)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"סיסמאות בסכנת חשיפה (חדשות)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"ייתכן שקיים סיכון"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"קיים סיכון"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"נמצאו סכנות אפשריות"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"נמצאו סיכונים"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"החשבון עלול להיות בסיכון"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"החשבון בסיכון"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{התראה #}two{# התראות}many{# התראות}other{# התראות}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"לא ניתן היה לפתוח את הדף"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"לא ניתן היה לפתור את הבעיה בהתראה"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ja/strings.xml b/SafetyCenter/Resources/shared_res/values-ja/strings.xml
index 5208f9d..af73387 100644
--- a/SafetyCenter/Resources/shared_res/values-ja/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ja/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"設定リストをご確認ください"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"デバイスが危険な可能性があります"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"デバイスが危険です"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"危険な可能性があります"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"危険にさらされています"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"データが危険な可能性があります"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"データが危険です"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"古いパスワードの不正使用"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"新しいパスワードの不正使用"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"危険にさらされています"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"危険にさらされています"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"潜在的なリスクが検出されました"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"リスクが検出されました"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"アカウントが危険な可能性があります"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"アカウントが危険です"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# 件のアラート}other{# 件のアラート}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"ページを開けませんでした"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"アラートを解決できませんでした"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ka/strings.xml b/SafetyCenter/Resources/shared_res/values-ka/strings.xml
index 4ecd544..bf14715 100644
--- a/SafetyCenter/Resources/shared_res/values-ka/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ka/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"პარამეტრების სიის შემოწმება"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"მოწყობილობა შესაძლო საფრთხის ქვეშაა"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"მოწყობილობა საფრთხის ქვეშაა"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"თქვენ შეიძლება საფრთხე გემუქრებოდეთ"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"თქვენ საფრთხეში ხართ"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"თქვ. მონაც. შესაძლოა საფრთხე ემუქრ."</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"თქვენ მონაცემებს საფრთხე ემუქრება"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"პაროლები კომპრომეტირებულია (ძველი)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"პაროლები გატეხილია (ახალი)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"თქვენ შეიძლება საფრთხე გემუქრებოდეთ"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"თქვენ საფრთხეში ხართ"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"ნაპოვნია პოტენციური რისკები"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"აღმოჩენილი რისკები"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"ანგარიშს შესაძლოა საფრთხე ემუქრება"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"ანგარიშს საფრთხე ემუქრება"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# გაფრთხილება}other{# გაფრთხილება}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"გვერდის გახსნა ვერ მოხერხდა"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"გაფრთხილება ვერ გადაიჭრა"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-kk/strings.xml b/SafetyCenter/Resources/shared_res/values-kk/strings.xml
index f295b8d..a9d11d0 100644
--- a/SafetyCenter/Resources/shared_res/values-kk/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-kk/strings.xml
@@ -25,8 +25,22 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Параметрлер тізімін тексеру"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Құрылғыға қауіп төнген болуы мүмкін"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Құрылғыға қауіп төніп тұр"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Қауіп төнуі мүмкін"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Қауіп төніп тұр"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Деректерге қауіп төнген сияқты"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Деректерге қауіп төніп тұр"</string>
+    <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) -->
+    <skip />
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Аккаунтқа қауіп төнген сияқты"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Аккаунтқа қауіп төніп тұр"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# хабарландыру}other{# хабарландыру}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Бет ашылмады."</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Хабарландыруда көрсетілген мәселе шешілмеді."</string>
diff --git a/SafetyCenter/Resources/shared_res/values-km/strings.xml b/SafetyCenter/Resources/shared_res/values-km/strings.xml
index 8b9448e..a36b062 100644
--- a/SafetyCenter/Resources/shared_res/values-km/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-km/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ពិនិត្យបញ្ជីការកំណត់"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ឧបករណ៍អាចប្រឈម​នឹងហានិភ័យ"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ឧបករណ៍​កំពុងប្រឈមនឹង​ហានិភ័យ"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"អ្នក​អាច​ប្រឈម​នឹង​ហានិភ័យ"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"អ្នក​ប្រឈម​នឹង​ហានិភ័យ"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ទិន្នន័យរបស់អ្នកអាចប្រឈមនឹងហានិភ័យ"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"ទិន្នន័យរបស់អ្នកគឺប្រឈមនឹងហានិភ័យ"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"ពាក្យសម្ងាត់រងការលុកលុយ (ចាស់)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"ពាក្យសម្ងាត់រងការលុកលុយ (ថ្មី)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"អ្នក​អាច​ប្រឈម​នឹង​ហានិភ័យ"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"អ្នក​ប្រឈម​នឹង​ហានិភ័យ"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"រកឃើញហានិភ័យដែលអាចមាន"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"រកឃើញហានិភ័យ"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"គណនីអាចប្រឈមនឹងហានិភ័យ"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"គណនីប្រឈមនឹងហានិភ័យ"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{ការជូនដំណឹង #}other{ការជូនដំណឹង #}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"មិនអាច​បើកទំព័រ​បានទេ"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"មិនអាច​ដោះស្រាយការជូនដំណឹងនេះ​បានទេ"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-kn/strings.xml b/SafetyCenter/Resources/shared_res/values-kn/strings.xml
index c02fe8f..f8198af 100644
--- a/SafetyCenter/Resources/shared_res/values-kn/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-kn/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ಸೆಟ್ಟಿಂಗ್‌ಗಳ ಪಟ್ಟಿಯನ್ನು ಪರಿಶೀಲಿಸಿ"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ಸಾಧನವು ಅಪಾಯದಲ್ಲಿರಬಹುದು"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ಸಾಧನವು ಅಪಾಯದಲ್ಲಿದೆ"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"ನೀವು ಅಪಾಯಕ್ಕೀಡಾಗಬಹುದು"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"ನೀವು ಅಪಾಯದಲ್ಲಿದ್ದೀರಿ"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ನಿಮ್ಮ ಡೇಟಾ ಅಪಾಯದಲ್ಲಿರಬಹುದು"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"ನಿಮ್ಮ ಡೇಟಾ ಅಪಾಯದಲ್ಲಿದೆ"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"ಅಪಾಯಕ್ಕೀಡಾದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು (ಹಳೆಯ)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"ಅಪಾಯಕ್ಕೀಡಾದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು (ಹೊಸತು)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"ನೀವು ಅಪಾಯಕ್ಕೀಡಾಗಿರಬಹುದು"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"ನೀವು ಅಪಾಯದಲ್ಲಿದ್ದೀರಿ"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"ಸಂಭವನೀಯ ಅಪಾಯಗಳು ಕಂಡುಬಂದಿವೆ"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"ಅಪಾಯಗಳು ಕಂಡುಬಂದಿವೆ"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"ಖಾತೆಯು ಅಪಾಯಕ್ಕೀಡಾಗಬಹುದು"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"ಖಾತೆ ಅಪಾಯದಲ್ಲಿದೆ"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ಅಲರ್ಟ್}one{# ಅಲರ್ಟ್‌ಗಳು}other{# ಅಲರ್ಟ್‌ಗಳು}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"ಪುಟವನ್ನು ತೆರೆಯಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"ಅಲರ್ಟ್ ಅನ್ನು ಬಗೆಹರಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ko/strings.xml b/SafetyCenter/Resources/shared_res/values-ko/strings.xml
index a9644d3..1674c17 100644
--- a/SafetyCenter/Resources/shared_res/values-ko/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ko/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"설정 목록 확인"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"기기에 잠재적 위험 발생"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"기기 위험 발생"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"위험에 노출되었을 수 있음"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"위험에 노출됨"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"데이터에 보안 위험이 있을 수 있음"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"데이터에 보안 위험이 있음"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"기존 비밀번호 손상됨"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"신규 비밀번호 손상됨"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"위험에 노출되었을 수 있음"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"위험에 노출됨"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"잠재적 위험 발견됨"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"위험 발견됨"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"계정에 보안 위험이 있을 수 있음"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"계정에 보안 위험이 있음"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{알림 #개}other{알림 #개}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"페이지를 열 수 없습니다."</string>
     <string name="resolving_action_error" msgid="371968886143262375">"알림을 해결할 수 없습니다."</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ky/strings.xml b/SafetyCenter/Resources/shared_res/values-ky/strings.xml
index 841212a..a29fe35 100644
--- a/SafetyCenter/Resources/shared_res/values-ky/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ky/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Параметрлердин тизмесин текшерүү"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Түзмөк коркунучта болушу мүмкүн"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Түзмөк коркунучта"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Коопсуздук коркунучта болушу мүмкүн"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Коопсуздук коркунучта"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Маалыматтын коопсуздугу коркунучта"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Маалыматтын коопсуздугу коркунучта"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Сырсөздөр уурдалды (эски)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Сырсөз уурдалды (жаңы)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Коопсуздук коркунучта"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Коопсуздук коркунучта"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Мүмкүн болгон коркунучтар табылды"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Коркунучтар табылды"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Аккаунт коркунучта болушу мүмкүн"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Аккаунттун коопсуздугу коркунучта"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# эскертүү}other{# эскертүү}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Барак ачылган жок"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Эскертүү чечилген жок"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-lo/strings.xml b/SafetyCenter/Resources/shared_res/values-lo/strings.xml
index 891ddde..e0f6f0f 100644
--- a/SafetyCenter/Resources/shared_res/values-lo/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-lo/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ກວດລາຍການການຕັ້ງຄ່າ"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ອຸປະກອນອາດມີຄວາມສ່ຽງ"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ອຸປະກອນມີຄວາມສ່ຽງ"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"ທ່ານອາດຢູ່ໃນຄວາມສ່ຽງ"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"ທ່ານຢູ່ໃນຄວາມສ່ຽງ"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ຂໍ້ມູນຂອງທ່ານອາດຕົກຢູ່ໃນຄວາມສ່ຽງ"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"ຂໍ້ມູນຂອງທ່ານຕົກຢູ່ໃນຄວາມສ່ຽງ"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"ລະຫັດຜ່ານມີການຮົ່ວໄຫຼ (ເກົ່າ)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"ລະຫັດຜ່ານມີການຮົ່ວໄຫຼ (ໃໝ່)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"ທ່ານອາດຕົກຢູ່ໃນຄວາມສ່ຽງ"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"ທ່ານຕົກຢູ່ໃນຄວາມສ່ຽງ"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"ພົບຄວາມສ່ຽງທີ່ອາດເກີດຂຶ້ນ"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"ພົບຄວາມສ່ຽງ"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"ບັນຊີອາດຕົກຢູ່ໃນຄວາມສ່ຽງ"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"ບັນຊີຕົກຢູ່ໃນຄວາມສ່ຽງ"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ແຈ້ງເຕືອນ}other{# ແຈ້ງເຕືອນ}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"ບໍ່ສາມາດເປີດໜ້າໄດ້"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"ບໍ່ສາມາດແກ້ໄຂແຈ້ງເຕືອນໄດ້"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-lt/strings.xml b/SafetyCenter/Resources/shared_res/values-lt/strings.xml
index bcf2623..bbef2ce 100644
--- a/SafetyCenter/Resources/shared_res/values-lt/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-lt/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Tikrinti nustatymų sąrašą"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Įrenginiui gali grėsti pavojus"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Įrenginiui gresia pavojus"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Jums gali kilti pavojus"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Jums iškilo pavojus"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Jūsų duomenims galėjo kilti pavojus"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Jūsų duomenims kilo pavojus"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Slaptažodžiai pažeisti (seni)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Slaptažodžiai pažeisti (nauji)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Jums gali kilti pavojus"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Jums iškilo pavojus"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Aptikta potencialių pavojų"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Aptikta pavojų"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Paskyrai galėjo kilti pavojus"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Paskyrai kilo pavojus"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# įspėjimas}one{# įspėjimas}few{# įspėjimai}many{# įspėjimo}other{# įspėjimų}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Nepavyko atidaryti puslapio"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Nepavyko pašalinti įspėjimo"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-lv/strings.xml b/SafetyCenter/Resources/shared_res/values-lv/strings.xml
index e40984b..b3aaec7 100644
--- a/SafetyCenter/Resources/shared_res/values-lv/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-lv/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Pārbaudiet iestatījumu sarakstu"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Iespējams, ierīce ir apdraudēta"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Ierīce ir apdraudēta"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Iespējams, jūs esat apdraudēts"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Jūs esat apdraudēts"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Jūsu dati var būt apdraudēti"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Jūsu dati ir apdraudēti"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Uzlauztas paroles (vecas)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Uzlauztas paroles (jaunas)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Iespējams, jūs esat apdraudēts"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Jūs esat apdraudēts"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Konstatēts iespējams risks"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Konstatēts risks"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Konts var būt apdraudēts"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Konts ir apdraudēts"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ieteikums vai brīdinājums}zero{# ieteikumu vai brīdinājumu}one{# ieteikums vai brīdinājums}other{# ieteikumi vai brīdinājumi}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Nevarēja atvērt lapu"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Nevarēja atrisināt ieteikumu vai brīdinājumu"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-mk/strings.xml b/SafetyCenter/Resources/shared_res/values-mk/strings.xml
index 20bd3f0..446b374 100644
--- a/SafetyCenter/Resources/shared_res/values-mk/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-mk/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Проверете го списокот со поставки"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Уредот можеби е изложен на ризик"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Уредот е изложен на ризик"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Можеби сте под ризик"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Под ризик сте"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Податоците можеби се под ризик"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Податоците се под ризик"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Компромитирани лозинки (стари)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Компромитирани лозинки (нови)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Можеби сте под ризик"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Под ризик сте"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Најдени се потенцијални ризици"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Најдени се ризици"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Сметката можеби е под ризик"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Сметката е под ризик"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# предупредување}one{# предупредување}other{# предупредувања}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Не можеше да се отвори страницата"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Не можеше да се реши предупредувањето"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ml/strings.xml b/SafetyCenter/Resources/shared_res/values-ml/strings.xml
index 6d6e602..62586aa 100644
--- a/SafetyCenter/Resources/shared_res/values-ml/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ml/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ക്രമീകരണ ലിസ്റ്റ് പരിശോധിക്കുക"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ഉപകരണം അപകടത്തിലാകാൻ സാധ്യതയുണ്ട്"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ഉപകരണം അപകടത്തിലാണ്"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"നിങ്ങൾ അപകടത്തിലായിരിക്കാം"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"നിങ്ങൾ അപകടത്തിലാണ്"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"നിങ്ങളുടെ ഡാറ്റ അപകടത്തിലായേക്കാം"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"നിങ്ങളുടെ ഡാറ്റ അപകടത്തിലാണ്"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"പാസ്‌വേഡുകൾ അപഹരിച്ചു (പഴയത്)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"പാസ്‌വേഡുകൾ അപഹരിച്ചു (പുതിയത്)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"നിങ്ങൾ അപകടത്തിലായിരിക്കാം"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"നിങ്ങൾ അപകടത്തിലാണ്"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"സാധ്യതയുള്ള അപകടസാധ്യതകൾ കണ്ടെത്തി"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"അപകടസാധ്യതകൾ കണ്ടെത്തി"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"അക്കൗണ്ട് അപകടത്തിലായേക്കാം"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"അക്കൗണ്ട് അപകടത്തിലാണ്"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# മുന്നറിയിപ്പ്}other{# മുന്നറിയിപ്പുകൾ}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"പേജ് തുറക്കാനായില്ല"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"മുന്നറിയിപ്പ് പരിഹരിക്കാനായില്ല"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-mn/strings.xml b/SafetyCenter/Resources/shared_res/values-mn/strings.xml
index 558fd90..def5733 100644
--- a/SafetyCenter/Resources/shared_res/values-mn/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-mn/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Тохиргооны жагсаалтыг шалгах"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Төхөөрөмж эрсдэлд байж магадгүй"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Төхөөрөмж эрсдэлд байна"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Та эрсдэлд байж магадгүй"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Та эрсдэлд байна"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Таны өгөгдөл эрсдэлд байж магадгүй"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Таны өгөгдөл эрсдэлд байна"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Нууц үг алдагдсан (хуучин)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Нууц үг алдагдсан (шинэ)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Та эрсдэлд орсон байж магадгүй"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Та эрсдэлд орсон байна"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Боломжит эрсдэл илэрсэн"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Эрсдэл илэрсэн"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Бүртгэл эрсдэлд байж магадгүй"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Бүртгэл эрсдэлд байна"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# сэрэмжлүүлэг}other{# сэрэмжлүүлэг}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Хуудсыг нээж чадсангүй"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Сэрэмжлүүлгийг шийдвэрлэж чадсангүй"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-mr/strings.xml b/SafetyCenter/Resources/shared_res/values-mr/strings.xml
index 59e308c..143751f 100644
--- a/SafetyCenter/Resources/shared_res/values-mr/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-mr/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"सेटिंग्जची सूची तपासा"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"डिव्हाइस धोक्यात असू शकते"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"डिव्हाइस धोक्यात आहे"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"तुमची सुरक्षा धोक्यात असू शकते"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"तुमची सुरक्षा धोक्यात आहे"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"तुमचा डेटा धोक्यात असू शकतो"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"तुमचा डेटा धोक्यात आहे"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"धोक्यात असलेले पासवर्ड (जुने)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"धोक्यात असलेले पासवर्ड (नवीन)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"तुमची सुरक्षा धोक्यात असू शकते"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"तुमची सुरक्षा धोक्यात आहे"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"संभाव्य धोके आढळले"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"धोके आढळले"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"खाते धोक्यात असू शकते"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"खाते धोक्यात आहे"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# इशारा}other{# इशारे}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"पेज उघडता आले नाही"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"इशाऱ्याचे निराकरण करता आले नाही"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ms/strings.xml b/SafetyCenter/Resources/shared_res/values-ms/strings.xml
index e11a8a2..8eb9c1e 100644
--- a/SafetyCenter/Resources/shared_res/values-ms/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ms/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Semak senarai tetapan"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Peranti mungkin berisiko"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Peranti berisiko"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Anda mungkin berisiko"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Anda berisiko"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Data anda mungkin berisiko"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Data anda berisiko"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Kata laluan terjejas (lama)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Kata laluan terjejas (baharu)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Anda mungkin berisiko"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Anda berisiko"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Risiko berpotensi ditemukan"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risiko ditemukan"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Akaun mungkin berisiko"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Akaun berisiko"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# amaran}other{# amaran}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Tidak dapat membuka halaman"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Tidak dapat menyelesaikan amaran"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-my/strings.xml b/SafetyCenter/Resources/shared_res/values-my/strings.xml
index 4156cf4..a09b3f6 100644
--- a/SafetyCenter/Resources/shared_res/values-my/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-my/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ဆက်တင်များစာရင်းကို စစ်ဆေးပါ"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"စက်တွင် အန္တရာယ်ရှိနိုင်သည်"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"စက်တွင် အန္တရာယ်ရှိနေသည်"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"သင့်တွင် အန္တရာယ်ရှိနိုင်သည်"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"သင့်တွင် အန္တရာယ်ရှိနေသည်"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"သင့်ဒေတာတွင် အန္တရာယ်ရှိနိုင်သည်"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"သင့်ဒေတာတွင် အန္တရာယ်ရှိသည်"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"စကားဝှက် ကျိုးပေါက်နေသည် (အဟောင်း)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"စကားဝှက် ကျိုးပေါက်နေသည် (အသစ်)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"သင့်တွင် အန္တရာယ်ရှိနိုင်သည်"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"သင့်တွင် အန္တရာယ်ရှိနေသည်"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"အန္တရာယ်ဖြစ်နိုင်ခြေ တွေ့သည်"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"အန္တရာယ် တွေ့ထားသည်"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"အကောင့်တွင် အန္တရာယ်ရှိနိုင်သည်"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"အကောင့်တွင် အန္တရာယ်ရှိသည်"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{သတိပေးချက် # ခု}other{သတိပေးချက် # ခု}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"စာမျက်နှာကို ဖွင့်၍မရပါ"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"သတိပေးချက်ကို ဖြေရှင်း၍မရပါ"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-nb/strings.xml b/SafetyCenter/Resources/shared_res/values-nb/strings.xml
index d6b0407..2bd116b 100644
--- a/SafetyCenter/Resources/shared_res/values-nb/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-nb/strings.xml
@@ -25,8 +25,22 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Sjekk innstillingslisten"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Enheten kan være i faresonen"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Enheten er i faresonen"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Du kan være i faresonen"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Du er i faresonen"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Dataene dine kan være i faresonen"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Dataene dine er i faresonen"</string>
+    <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) -->
+    <skip />
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Kontoen kan være i faresonen"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Kontoen er i faresonen"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# varsel}other{# varsler}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Kunne ikke åpne siden"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Kunne ikke løse varselet"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ne/strings.xml b/SafetyCenter/Resources/shared_res/values-ne/strings.xml
index c415a7e..7e2cdd2 100644
--- a/SafetyCenter/Resources/shared_res/values-ne/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ne/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"सेटिङको सूची जाँच्नुहोस्"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"डिभाइस जोखिममा हुन सक्छ"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"डिभाइस जोखिममा छ"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"सुरक्षा र गोपनीयता जोखिममा हुन सक्छ"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"सुरक्षा र गोपनीयता जोखिममा छ"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"तपाईंको डेटा जोखिममा हुन सक्छ"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"तपाईंको डेटा जोखिममा छ"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"पासवर्ड ह्याक भएको छ (पुरानो)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"पासवर्ड ह्याक भएको छ (नयाँ पासवर्ड)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"तपाईंको सुरक्षा जोखिममा हुन सक्छ"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"तपाईंको खाता जोखिममा छ"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"सम्भावित जोखिम फेला परे"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"जोखिम फेला परे"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"खाता जोखिममा हुन सक्छ"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"खाता जोखिममा छ"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# अलर्ट}other{# वटा अलर्ट}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"पेज खोल्न सकिएन"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"अलर्ट समाधान गर्न सकिएन"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-nl/strings.xml b/SafetyCenter/Resources/shared_res/values-nl/strings.xml
index 25902b9..4ca8882 100644
--- a/SafetyCenter/Resources/shared_res/values-nl/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-nl/strings.xml
@@ -25,8 +25,22 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Check de lijst met instellingen"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Apparaat loopt misschien gevaar"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Apparaat loopt gevaar"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Je loopt misschien risico"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Je loopt risico"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Je gegevens lopen misschien gevaar"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Je gegevens lopen risico"</string>
+    <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) -->
+    <skip />
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Account loopt misschien gevaar"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Account loopt risico"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# melding}other{# meldingen}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Kan de pagina niet openen"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Kan melding niet oplossen"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-or/strings.xml b/SafetyCenter/Resources/shared_res/values-or/strings.xml
index 8af4ad1..574d5f7 100644
--- a/SafetyCenter/Resources/shared_res/values-or/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-or/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ସେଟିଂସ ତାଲିକା ଯାଞ୍ଚ କରନ୍ତୁ"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ଡିଭାଇସଟି ରିସ୍କରେ ଥାଇପାରେ"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ଡିଭାଇସଟି ରିସ୍କରେ ଅଛି"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"ଆପଣ ବିପଦରେ ଥାଇପାରନ୍ତି"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"ଆପଣ ବିପଦରେ ଅଛନ୍ତି"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ଆପଣଙ୍କ ଡାଟା ବିପଦରେ ଥାଇପାରେ"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"ଆପଣଙ୍କ ଡାଟା ବିପଦରେ ଅଛି"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"ପାସୱାର୍ଡ ଚୋରି ହୋଇଯାଇଛି (ପୁରୁଣା)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"ପାସୱାର୍ଡ ଚୋରି ହୋଇଯାଇଛି (ନୂଆ)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"ଆପଣ ବିପଦରେ ଥାଇପାରନ୍ତି"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"ଆପଣ ବିପଦରେ ଅଛନ୍ତି"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"ସମ୍ଭାବ୍ୟ ବିପଦ ମିଳିଛି"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"ବିପଦ ମିଳିଛି"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"ଆକାଉଣ୍ଟ ବିପଦରେ ଥାଇପାରେ"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"ଆକାଉଣ୍ଟ ବିପଦରେ ଅଛି"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{#ଟି ଆଲର୍ଟ}other{#ଟି ଆଲର୍ଟ}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"ପୃଷ୍ଠାକୁ ଖୋଲା ଯାଇପାରିଲା ନାହିଁ"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"ଆଲର୍ଟର ସମାଧାନ କରାଯାଇପାରିଲା ନାହିଁ"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-pa/strings.xml b/SafetyCenter/Resources/shared_res/values-pa/strings.xml
index 2405a41..55cc2ad 100644
--- a/SafetyCenter/Resources/shared_res/values-pa/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-pa/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ਸੈਟਿੰਗਾਂ ਸੂਚੀ ਦੀ ਜਾਂਚ ਕਰੋ"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"ਡੀਵਾਈਸ ਜੋਖਮ ਵਿੱਚ ਹੋ ਸਕਦਾ ਹੈ"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"ਡੀਵਾਈਸ ਜੋਖਮ ਵਿੱਚ ਹੈ"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਜੋਖਮ ਵਿੱਚ ਹੋ ਸਕਦੀ ਹੈ"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਜੋਖਮ ਵਿੱਚ ਹੈ"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ਤੁਹਾਡਾ ਡਾਟਾ ਜੋਖਮ ਵਿੱਚ ਹੋ ਸਕਦਾ ਹੈ"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"ਤੁਹਾਡਾ ਡਾਟਾ ਜੋਖਮ ਵਿੱਚ ਹੈ"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"ਪਾਸਵਰਡਾਂ ਨਾਲ ਛੇੜਛਾੜ ਹੋਈ (ਪੁਰਾਣਾ)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"ਪਾਸਵਰਡਾਂ ਨਾਲ ਛੇੜਛਾੜ ਹੋਈ (ਨਵਾਂ)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਜੋਖਮ ਵਿੱਚ ਹੋ ਸਕਦੀ ਹੈ"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਜੋਖਮ ਵਿੱਚ ਹੈ"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"ਸੰਭਾਵੀ ਜੋਖਮਾਂ ਦਾ ਪਤਾ ਲੱਗਿਆ"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"ਜੋਖਮਾਂ ਦਾ ਪਤਾ ਲੱਗਿਆ"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"ਖਾਤਾ ਜੋਖਮ ਵਿੱਚ ਹੋ ਸਕਦਾ ਹੈ"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"ਖਾਤਾ ਜੋਖਮ ਵਿੱਚ ਹੈ"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ਸੁਚੇਤਨਾ}one{# ਸੁਚੇਤਨਾ}other{# ਸੁਚੇਤਨਾਵਾਂ}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"ਪੰਨਾ ਖੋਲ੍ਹਿਆ ਨਹੀਂ ਜਾ ਸਕਿਆ"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"ਸੁਚੇਤਨਾ ਦਾ ਹੱਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-pl/strings.xml b/SafetyCenter/Resources/shared_res/values-pl/strings.xml
index bf02da6..d3838c9 100644
--- a/SafetyCenter/Resources/shared_res/values-pl/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-pl/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Sprawdź listę ustawień"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Urządzenie może być zagrożone"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Urządzenie jest zagrożone"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Twoje bezpieczeństwo może być zagrożone"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Twoje bezpieczeństwo jest zagrożone"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Dane mogą być zagrożone"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Dane są zagrożone"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Przejęte hasła (stare)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Przejęte hasła (nowe)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Twoje bezpieczeństwo może być zagrożone"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Twoje bezpieczeństwo jest zagrożone"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Wykryte potencjalne zagrożenia"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Wykryte zagrożenia"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Konto może być zagrożone"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Konto jest zagrożone"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alert}few{# alerty}many{# alertów}other{# alertu}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Nie udało się otworzyć strony"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Nie udało się rozwiązać problemu z alertu"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-pt-rBR/strings.xml b/SafetyCenter/Resources/shared_res/values-pt-rBR/strings.xml
index d833b3a..e2d4423 100644
--- a/SafetyCenter/Resources/shared_res/values-pt-rBR/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-pt-rBR/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Verificar lista de configurações"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"O dispositivo pode estar em risco"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"O dispositivo está em risco"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Você pode estar em risco"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Você está em risco"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Seus dados podem estar em risco"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Seus dados estão em risco"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Senhas comprometidas (antigas)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Senhas comprometidas (novas)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Você pode estar em risco"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Você está em risco"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Possíveis riscos encontrados"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Riscos encontrados"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"A conta pode estar em risco"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"A conta está em risco"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerta}one{# alerta}many{# de alertas}other{# alertas}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Não foi possível abrir a página"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Não foi possível resolver o alerta"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-pt-rPT/strings.xml b/SafetyCenter/Resources/shared_res/values-pt-rPT/strings.xml
index 587e3f0..329fb9e 100644
--- a/SafetyCenter/Resources/shared_res/values-pt-rPT/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-pt-rPT/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Verifique a lista de definições"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"O dispositivo pode estar em risco"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"O dispositivo está em risco"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Pode estar em risco"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Está em risco"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Os seus dados podem estar em risco"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Os seus dados estão em risco"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Pal.-passe antigas comprometidas"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Palavras-passe novas comprometidas"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Pode estar em risco"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Está em risco"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potenciais riscos encontrados"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Riscos encontrados"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"A conta pode estar em risco"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"A conta está em risco"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerta}many{# alertas}other{# alertas}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Não foi possível abrir a página"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Não foi possível resolver o alerta"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-pt/strings.xml b/SafetyCenter/Resources/shared_res/values-pt/strings.xml
index d833b3a..e2d4423 100644
--- a/SafetyCenter/Resources/shared_res/values-pt/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-pt/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Verificar lista de configurações"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"O dispositivo pode estar em risco"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"O dispositivo está em risco"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Você pode estar em risco"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Você está em risco"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Seus dados podem estar em risco"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Seus dados estão em risco"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Senhas comprometidas (antigas)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Senhas comprometidas (novas)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Você pode estar em risco"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Você está em risco"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Possíveis riscos encontrados"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Riscos encontrados"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"A conta pode estar em risco"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"A conta está em risco"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerta}one{# alerta}many{# de alertas}other{# alertas}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Não foi possível abrir a página"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Não foi possível resolver o alerta"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ro/strings.xml b/SafetyCenter/Resources/shared_res/values-ro/strings.xml
index e55971a..21fc3dd 100644
--- a/SafetyCenter/Resources/shared_res/values-ro/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ro/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Verifică lista de setări"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Dispozitivul poate fi expus la risc"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Dispozitivul este expus riscului"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"E posibil să fii expus(ă) la risc"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Ești expus(ă) la risc"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Datele tale pot fi în pericol"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Datele tale sunt în pericol"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Parole compromise (vechi)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Parole compromise (noi)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"E posibil să fii expus(ă) la risc"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Ești expus(ă) la risc"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"S-au găsit riscuri potențiale"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Riscuri descoperite"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Contul poate fi în pericol"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Contul este în pericol"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alertă}few{# alerte}other{# de alerte}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Pagina nu s-a putut deschide"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Nu s-a putut rezolva alerta"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ru/strings.xml b/SafetyCenter/Resources/shared_res/values-ru/strings.xml
index d5cc573..cdf68fd 100644
--- a/SafetyCenter/Resources/shared_res/values-ru/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ru/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Изучите список настроек"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Устройство может быть под угрозой"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Устройство под угрозой"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Безопасность может быть под угрозой"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Ваша безопасность под угрозой"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Ваши данные могут быть под угрозой"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Ваши данные под угрозой"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Раскрыты старые пароли"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Раскрыты новые пароли"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Безопасность может быть под угрозой"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Ваша безопасность под угрозой"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Обнаружены потенциальные риски"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Обнаружены риски"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Аккаунт может быть под угрозой"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Аккаунт под угрозой"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# оповещение}one{# оповещение}few{# оповещения}many{# оповещений}other{# оповещения}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Не удалось открыть страницу."</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Не удалось устранить проблему."</string>
diff --git a/SafetyCenter/Resources/shared_res/values-si/strings.xml b/SafetyCenter/Resources/shared_res/values-si/strings.xml
index 76925cc..80ecaa3 100644
--- a/SafetyCenter/Resources/shared_res/values-si/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-si/strings.xml
@@ -25,8 +25,22 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"සැකසීම් ලැයිස්තුව පරීක්ෂා කරන්න"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"උපාංගය අවදානමේ තිබිය හැක"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"උපාංගය අවදානමේ ඇත"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"ඔබ අවදානමේ සිටිය හැක"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"ඔබ අවදානමේ සිටියි"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ඔබේ දත්ත අවදානමට ලක් විය හැක"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"ඔබේ දත්ත අවදානමේ ඇත"</string>
+    <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) -->
+    <skip />
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"ගිණුම අවදානමේ තිබිය හැක"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"ගිණුම අවදානමේ පවතී"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ඇඟවීමක්}one{ඇඟවීම් #ක්}other{ඇඟවීම් #ක්}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"පිටුව විවෘත කළ නොහැකි විය"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"ඇඟවීම විසඳිය නොහැකි විය"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-sk/strings.xml b/SafetyCenter/Resources/shared_res/values-sk/strings.xml
index 0853219..43166ed 100644
--- a/SafetyCenter/Resources/shared_res/values-sk/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-sk/strings.xml
@@ -25,8 +25,22 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Kontrolný zoznam nastavení"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Zariadenie môže byť ohrozené"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Zariadenie je ohrozené"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Vaše zabezpečenie môže byť ohrozené"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Vaše zabezpečenie je ohrozené"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Vaše údaje môžu byť ohrozené"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Vaše údaje sú ohrozené"</string>
+    <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) -->
+    <skip />
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Účet môže byť ohrozený"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Účet je ohrozený"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# upozornenie}few{# upozornenia}many{# alerts}other{# upozornení}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Stránku sa nepodarilo otvoriť"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Upozornenie sa nepodarilo vyriešiť"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-sl/strings.xml b/SafetyCenter/Resources/shared_res/values-sl/strings.xml
index 58c7f08..7ce5e01 100644
--- a/SafetyCenter/Resources/shared_res/values-sl/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-sl/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Seznam za preverjanje nastavitev"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Naprava je morda ogrožena"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Naprava je ogrožena"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Morda ste ogroženi"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Ste ogroženi"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Podatki so morda ogroženi"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Podatki so ogroženi"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Gesla so ogrožena (stara)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Gesla so ogrožena (nova)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Morda ste ogroženi"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Ste ogroženi"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Najdena so morebitna tveganja"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Najdena so tveganja"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Račun je morda ogrožen"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Račun je ogrožen"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# opozorilo}one{# opozorilo}two{# opozorili}few{# opozorila}other{# opozoril}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Strani ni bilo mogoče odpreti."</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Opozorila ni bilo mogoče odpraviti."</string>
diff --git a/SafetyCenter/Resources/shared_res/values-sq/strings.xml b/SafetyCenter/Resources/shared_res/values-sq/strings.xml
index eb4f0ef..9d40b86 100644
--- a/SafetyCenter/Resources/shared_res/values-sq/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-sq/strings.xml
@@ -25,8 +25,22 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Kontrollo listën e cilësimeve"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Pajisja mund të jetë në rrezik"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Pajisja është në rrezik"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Mund të jesh në rrezik"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Je në rrezik"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Të dhënat e tua mund të jenë në rrezik"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Të dhënat e tua janë në rrezik"</string>
+    <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) -->
+    <skip />
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Llogaria mund të jetë në rrezik"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Llogaria është në rrezik"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# sinjalizim}other{# sinjalizime}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Faqja nuk mund të hapej"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Sinjalizimi nuk mund të zgjidhej"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-sr/strings.xml b/SafetyCenter/Resources/shared_res/values-sr/strings.xml
index 629387a..9bd52b9 100644
--- a/SafetyCenter/Resources/shared_res/values-sr/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-sr/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Проверите листу подешавања"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Уређај је можда угрожен"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Уређај је угрожен"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Можда сте угрожени"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Угрожени сте"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Подаци су можда угрожени"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Подаци су угрожени"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Лозинке су угрожене (старе)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Лозинке су угрожене (нове)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Можда сте угрожени"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Угрожени сте"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Пронађени су потенцијални ризици"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Пронађени су ризици"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Налог је можда угрожен"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Налог је угрожен"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# обавештење}one{# обавештење}few{# обавештења}other{# обавештења}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Отварање странице није успело"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Решавање обавештења није успело"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-sv/strings.xml b/SafetyCenter/Resources/shared_res/values-sv/strings.xml
index 42c5060..5cf5f34 100644
--- a/SafetyCenter/Resources/shared_res/values-sv/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-sv/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Kontrollera listan med inställningar"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Enheten kan vara i fara"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Enheten är i fara"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Du kan vara utsatt för fara"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Du är i fara"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Din data kan vara i fara"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Din data är i fara"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Utsatta lösenord (gamla)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Utsatta lösenord (nya)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Du kan vara i riskzonen"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Du är i riskzonen"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potentiella risker har upptäckts"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risker har upptäckts"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Kontot kan vara i fara"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Kontot är i fara"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# varning}other{# varningar}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Det gick inte att öppna sidan"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Det gick inte att åtgärda varningen"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-sw/strings.xml b/SafetyCenter/Resources/shared_res/values-sw/strings.xml
index 7d02aa6..6dd5418 100644
--- a/SafetyCenter/Resources/shared_res/values-sw/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-sw/strings.xml
@@ -25,8 +25,22 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Kagua orodha ya mipangilio"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Huenda kifaa kikawa hatarini"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Kifaa kiko hatarini"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Huenda uko hatarini"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Uko hatarini"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Huenda data yako iko hatarini"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Data yako iko hatarini"</string>
+    <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) -->
+    <skip />
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Huenda akaunti iko hatarini"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Akaunti iko hatarini"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{Arifa #}other{Arifa #}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Imeshindwa kufungua ukurasa"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Imeshindwa kutia alama kuwa arifa imeshughulikiwa"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ta/strings.xml b/SafetyCenter/Resources/shared_res/values-ta/strings.xml
index e483473..a534d0e 100644
--- a/SafetyCenter/Resources/shared_res/values-ta/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ta/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"அமைப்புகள் பட்டியலைச் சரிபாருங்கள்"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"சாதனம் ஆபத்தில் இருக்கக்கூடும்"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"சாதனம் ஆபத்தில் உள்ளது"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"நீங்கள் ஆபத்தில் இருக்கலாம்"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"நீங்கள் ஆபத்தில் இருக்கிறீர்கள்"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"உங்கள் தரவு ஆபத்தில் இருக்கலாம்"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"உங்கள் தரவு ஆபத்தில் உள்ளது"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"கடவுச்சொற்கள் களவாடப்பட்டுள்ளன (பழையவை)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"கடவுச்சொற்கள் களவாடப்பட்டுள்ளன (புதியவை)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"நீங்கள் ஆபத்தில் இருக்கலாம்"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"நீங்கள் ஆபத்தில் இருக்கிறீர்கள்"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"சாத்தியமுள்ள ஆபத்து கண்டறியப்பட்டன"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"ஆபத்துகள் கண்டறியப்பட்டுள்ளன"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"கணக்கு ஆபத்தில் இருக்கலாம்"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"கணக்கு ஆபத்தில் உள்ளது"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# எச்சரிக்கை}other{# எச்சரிக்கைகள்}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"பக்கத்தைத் திறக்க முடியவில்லை"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"எச்சரிக்கையைத் தீர்க்க முடியவில்லை"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-te/strings.xml b/SafetyCenter/Resources/shared_res/values-te/strings.xml
index 9915b2b..98bbf9c 100644
--- a/SafetyCenter/Resources/shared_res/values-te/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-te/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"సెట్టింగ్‌ల లిస్ట్‌ను చెక్ చేయండి"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"పరికరం ప్రమాదంలో ఉండవచ్చు"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"పరికరం ప్రమాదంలో ఉంది"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"మీరు రిస్క్‌లో ఉండవచ్చు"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"మీరు రిస్క్‌లో ఉన్నారు"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"మీ డేటా ప్రమాదంలో ఉండవచ్చు"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"మీ డేటా ప్రమాదంలో ఉంది"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"(పాత) సురక్షితం కాని పాస్‌వర్డ్స్"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"(కొత్త) సురక్షితం కాని పాస్‌వర్డ్స్"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"మీరు రిస్క్‌లో ఉండవచ్చు"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"మీరు రిస్క్‌లో ఉన్నారు"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"అవకాశం ఉన్న రిస్క్‌లు కనుగొనబడ్డాయి"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"రిస్క్‌లు కనుగొనబడ్డాయి"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"ఖాతా ప్రమాదంలో ఉండవచ్చు"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"ఖాతా ప్రమాదంలో ఉంది"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# అలర్ట్}other{# అలర్ట్‌లు}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"పేజీని తెరవడం సాధ్యపడలేదు"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"అలర్ట్‌ను పరిష్కరించడం సాధ్యపడలేదు"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-th/strings.xml b/SafetyCenter/Resources/shared_res/values-th/strings.xml
index bc32ee0..c627090 100644
--- a/SafetyCenter/Resources/shared_res/values-th/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-th/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ตรวจสอบรายการการตั้งค่า"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"อุปกรณ์อาจมีความเสี่ยง"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"อุปกรณ์มีความเสี่ยง"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"คุณอาจมีความเสี่ยง"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"คุณกำลังมีความเสี่ยง"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"ข้อมูลของคุณอาจมีความเสี่ยง"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"ข้อมูลของคุณมีความเสี่ยง"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"รหัสผ่าน (เก่า) ถูกละเมิด"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"รหัสผ่าน (ใหม่) ถูกละเมิด"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"คุณอาจมีความเสี่ยง"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"คุณกำลังมีความเสี่ยง"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"พบความเสี่ยงที่อาจเกิดขึ้น"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"พบความเสี่ยง"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"บัญชีอาจมีความเสี่ยง"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"บัญชีมีความเสี่ยง"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{การแจ้งเตือน # รายการ}other{การแจ้งเตือน # รายการ}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"เปิดหน้าไม่ได้"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"แก้ไขการแจ้งเตือนไม่ได้"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-tl/strings.xml b/SafetyCenter/Resources/shared_res/values-tl/strings.xml
index 0f25207..12ab86d 100644
--- a/SafetyCenter/Resources/shared_res/values-tl/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-tl/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Tingnan ang listahan ng mga setting"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Posibleng nanganganib ang device"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Nanganganib ang device"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Posibleng nanganganib ka"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Nanganganib ka"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Posibleng nanganganib ang data mo"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Nanganganib ang iyong data"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Nakompromiso ang password (luma)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Nakompromiso ang password (bago)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Posibleng nanganganib ka"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Nanganganib ka"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"May nakitang potensyal na panganib"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"May nakitang mga panganib"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Posibleng nanganganib ang account"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Nanganganib ang account"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# alerto}one{# alerto}other{# na alerto}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Hindi mabuksan ang page"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Hindi ma-resolve ang alerto"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-tr/strings.xml b/SafetyCenter/Resources/shared_res/values-tr/strings.xml
index df7386e..3656c75 100644
--- a/SafetyCenter/Resources/shared_res/values-tr/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-tr/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Ayarlar listesini kontrol edin"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Cihaz risk altında olabilir"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Cihaz risk altında"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Risk altında olabilirsiniz"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Risk altındasınız"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Verileriniz risk altında olabilir"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Verileriniz risk altında"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Şifrelerin güvenliği ihlal edildi (eski)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Şifrelerin güvenliği ihlal edildi (yeni)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Risk altında olabilirsiniz"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Risk altındasınız"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Olası riskler bulundu"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Risk bulundu"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Hesap risk altında olabilir"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Hesap risk altında"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# uyarı}other{# uyarı}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Sayfa açılamadı"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Uyarı sonlandırılamadı"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-uk/strings.xml b/SafetyCenter/Resources/shared_res/values-uk/strings.xml
index 4f1c3df..09363cc 100644
--- a/SafetyCenter/Resources/shared_res/values-uk/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-uk/strings.xml
@@ -25,8 +25,22 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Перегляньте список налаштувань"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Можливо, пристрій під загрозою"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Пристрій під загрозою"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Можливо, ваша безпека під загрозою"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Ваша безпека під загрозою"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Можлива загроза даним"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Ваші дані під загрозою"</string>
+    <!-- no translation found for overall_severity_level_passwords_recommendation_title (8625105570296877719) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_passwords_warning_title (7859047988648851217) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_personal_recommendation_title (5493814377982623779) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_personal_warning_title (5070434468955164734) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_safety_recommendation_title (4291797434760242793) -->
+    <skip />
+    <!-- no translation found for overall_severity_level_critical_safety_warning_title (6666640109779586868) -->
+    <skip />
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Можлива загроза обліковому запису"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Обліковий запис під загрозою"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# сповіщення}one{# сповіщення}few{# сповіщення}many{# сповіщень}other{# сповіщення}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Не вдалося відкрити сторінку"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Не вдалося закрити сповіщення"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-ur/strings.xml b/SafetyCenter/Resources/shared_res/values-ur/strings.xml
index fd743f7..e6edf41 100644
--- a/SafetyCenter/Resources/shared_res/values-ur/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-ur/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"ترتیبات کی فہرست چیک کریں"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"آلہ خطرے میں ہو سکتا ہے"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"آلہ خطرے میں ہے"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"آپ خطرے میں ہو سکتے ہیں"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"آپ خطرے میں ہے"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"آپ کا ڈیٹا خطرے میں ہو سکتا ہے"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"آپ کا ڈیٹا خطرے میں ہے"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"متاثرہ پاس ورڈز (پرانا)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"متاثرہ پاس ورڈز (نیا)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"آپ خطرے میں ہو سکتے ہیں"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"آپ خطرے میں ہے"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"ممکنہ خطرات پائے گئے"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"خطرات پائے گئے"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"اکاؤنٹ خطرے میں ہو سکتا ہے"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"اکاؤنٹ خطرے میں ہے"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# الرٹ}other{# الرٹس}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"صفحہ نہیں کھل سکا"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"الرٹ حل نہیں ہو سکا"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-uz/strings.xml b/SafetyCenter/Resources/shared_res/values-uz/strings.xml
index 2c4010b..3995f49 100644
--- a/SafetyCenter/Resources/shared_res/values-uz/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-uz/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Sozlamalar roʻyxatini tekshirish"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Qurilma xavf ostida boʻlishi mumkin"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Qurilma xavf ostida"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Xavf ostida boʻlishingiz mumkin"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Xavf ostidasiz"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Maʼlumotlar xavf ostida shekilli"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Maʼlumotlaringiz xavf ostida"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Parollar oshkor qilindi (eski)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Parollar oshkor qilindi (yangi)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Xavf ostida boʻlishingiz mumkin"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Xavf ostidasiz"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Potentsial xavflar topildi"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Xavflar topildi"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Hisob xavf ostida shekilli"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Hisob xavf ostida"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# ta ogohlantirish}other{# ta ogohlantirish}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Sahifa ochilmadi"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Ogohlantirish hal qilinmadi"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-vi/strings.xml b/SafetyCenter/Resources/shared_res/values-vi/strings.xml
index dfcc286..f04eb06 100644
--- a/SafetyCenter/Resources/shared_res/values-vi/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-vi/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Kiểm tra danh sách cài đặt"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Thiết bị có thể gặp rủi ro"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Thiết bị đang gặp rủi ro"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Bạn có thể gặp rủi ro"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Bạn đang gặp rủi ro"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Dữ liệu của bạn có thể gặp rủi ro"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Dữ liệu của bạn đang gặp rủi ro"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Mật khẩu bị lộ (mật khẩu cũ)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Mật khẩu bị lộ (mật khẩu mới)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Bạn có thể gặp rủi ro"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Bạn đang gặp rủi ro"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Đã phát hiện rủi ro tiềm ẩn"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Đã phát hiện rủi ro"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"Tài khoản có thể gặp rủi ro"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"Tài khoản đang gặp rủi ro"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# cảnh báo}other{# cảnh báo}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Không thể mở trang"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Không thể giải quyết vấn đề cảnh báo"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-zh-rCN/strings.xml b/SafetyCenter/Resources/shared_res/values-zh-rCN/strings.xml
index 861a970..da7395d 100644
--- a/SafetyCenter/Resources/shared_res/values-zh-rCN/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-zh-rCN/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"请检查设置列表"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"设备可能存在风险"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"设备存在风险"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"您可能有风险"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"您目前有风险"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"您的数据可能存在风险"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"您的数据存在风险"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"密码被盗(旧密码)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"密码被盗(新密码)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"您可能有风险"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"您目前有风险"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"发现潜在风险"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"发现风险"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"帐号可能存在风险"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"帐号存在风险"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# 条提醒}other{# 条提醒}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"无法打开页面"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"无法解决提醒事项"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-zh-rHK/strings.xml b/SafetyCenter/Resources/shared_res/values-zh-rHK/strings.xml
index 051d345..420c5dd 100644
--- a/SafetyCenter/Resources/shared_res/values-zh-rHK/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-zh-rHK/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"查看設定清單"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"裝置可能存在風險"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"裝置存在風險"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"您可能面臨風險"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"您正面臨風險"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"您的資料可能面臨風險"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"您的資料正面臨風險"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"被盜用的密碼 (舊)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"被盜用的密碼 (新)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"您可能面臨風險"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"您正面臨風險"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"系統發現潛在風險"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"系統發現風險"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"帳戶可能面臨風險"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"裝置正面臨風險"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# 個警示}other{# 個警示}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"無法開啟頁面"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"無法解除警示"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-zh-rTW/strings.xml b/SafetyCenter/Resources/shared_res/values-zh-rTW/strings.xml
index 9b408bb..4bc2956 100644
--- a/SafetyCenter/Resources/shared_res/values-zh-rTW/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-zh-rTW/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"檢查設定清單"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"裝置可能有風險"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"裝置有風險"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"你可能有風險"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"你目前有風險"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"你的資料可能面臨風險"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"你的資料目前有風險"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"舊密碼已遭外洩"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"新密碼已遭外洩"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"你的帳戶可能有風險"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"你的帳戶有風險"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"發現潛在風險"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"發現風險"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"帳戶可能面臨風險"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"帳戶目前有風險"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{# 個警示}other{# 個警示}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"無法開啟網頁"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"無法解決警示"</string>
diff --git a/SafetyCenter/Resources/shared_res/values-zu/strings.xml b/SafetyCenter/Resources/shared_res/values-zu/strings.xml
index 7150779..637577a 100644
--- a/SafetyCenter/Resources/shared_res/values-zu/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-zu/strings.xml
@@ -25,8 +25,16 @@
     <string name="overall_severity_level_ok_review_summary" msgid="7743619617413076275">"Hlola uhlu lwamasethingi"</string>
     <string name="overall_severity_level_device_recommendation_title" msgid="5250040236433061827">"Idivayisi ingaba sengozini"</string>
     <string name="overall_severity_level_critical_device_warning_title" msgid="5901771721834272596">"Idivayisi isengozini"</string>
-    <string name="overall_severity_level_safety_recommendation_title" msgid="6436208984463981167">"Ungaba sengozini"</string>
-    <string name="overall_severity_level_critical_safety_warning_title" msgid="1039142045555227172">"Usengozini"</string>
+    <string name="overall_severity_level_data_recommendation_title" msgid="1424269714861655302">"Idatha yakho isengozini"</string>
+    <string name="overall_severity_level_critical_data_warning_title" msgid="1012704126634698604">"Idatha yakho isengozini"</string>
+    <string name="overall_severity_level_passwords_recommendation_title" msgid="8625105570296877719">"Amaphasiwedi onakalisiwe (amadala)"</string>
+    <string name="overall_severity_level_critical_passwords_warning_title" msgid="7859047988648851217">"Amaphasiwedi onakalisiwe (amasha)"</string>
+    <string name="overall_severity_level_personal_recommendation_title" msgid="5493814377982623779">"Kungenzeka usengozini"</string>
+    <string name="overall_severity_level_critical_personal_warning_title" msgid="5070434468955164734">"Usengozini"</string>
+    <string name="overall_severity_level_safety_recommendation_title" msgid="4291797434760242793">"Izingozi ezingaba khona zitholakele"</string>
+    <string name="overall_severity_level_critical_safety_warning_title" msgid="6666640109779586868">"Izingozi ezitholakele"</string>
+    <string name="overall_severity_level_account_recommendation_title" msgid="2267542168734275090">"I-akhawunti ingaba sengozini"</string>
+    <string name="overall_severity_level_critical_account_warning_title" msgid="4402278408972158353">"I-akhawunti isengcupheni"</string>
     <string name="overall_severity_n_alerts_summary" msgid="1105615451561197136">"{count,plural, =1{Isexwayiso esi-#}one{Izexwayiso ezingu-#}other{Izexwayiso ezingu-#}}"</string>
     <string name="redirecting_error" msgid="8146983632878233202">"Ayikwazanga ukuvula ikhasi"</string>
     <string name="resolving_action_error" msgid="371968886143262375">"Ayikwazanga ukuxazulula isexwayiso"</string>
diff --git a/SafetyCenter/Resources/shared_res/values/strings.xml b/SafetyCenter/Resources/shared_res/values/strings.xml
index b4fcebb..964812e 100644
--- a/SafetyCenter/Resources/shared_res/values/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values/strings.xml
@@ -40,11 +40,35 @@
     <!-- Title for the overall Safety Center status when the user security and privacy signals are putting them at risk [CHAR LIMIT=35] -->
     <string name="overall_severity_level_critical_device_warning_title">Device is at risk</string>
 
+    <!-- Title for the overall Safety Center status when the user's data could potentially be at risk [CHAR LIMIT=35] -->
+    <string name="overall_severity_level_data_recommendation_title">Your data may be at risk</string>
+
+    <!-- Title for the overall Safety Center status when the user's data is at risk [CHAR LIMIT=35] -->
+    <string name="overall_severity_level_critical_data_warning_title">Your data is at risk</string>
+
+    <!-- Title for the overall Safety Center status when the user's passwords could potentially be at risk (old or unused passwords) [CHAR LIMIT=35] -->
+    <string name="overall_severity_level_passwords_recommendation_title">Passwords compromised (old)</string>
+
+    <!-- Title for the overall Safety Center status when the user's passwords are at risk (new passwords) [CHAR LIMIT=35] -->
+    <string name="overall_severity_level_critical_passwords_warning_title">Passwords compromised (new)</string>
+
+    <!-- Title for the overall Safety Center status when the user's personal safety could potentially be at risk [CHAR LIMIT=35] -->
+    <string name="overall_severity_level_personal_recommendation_title">You may be at risk</string>
+
+    <!-- Title for the overall Safety Center status when the user's personal safety is at risk [CHAR LIMIT=35] -->
+    <string name="overall_severity_level_critical_personal_warning_title">You are at risk</string>
+
     <!-- Title for the overall Safety Center status when the user security and privacy signals could potentially put their general safety at risk [CHAR LIMIT=35] -->
-    <string name="overall_severity_level_safety_recommendation_title">You may be at risk</string>
+    <string name="overall_severity_level_safety_recommendation_title">Potential risks found</string>
 
     <!-- Title for the overall Safety Center status when the user security and privacy signals are putting their general safety at risk [CHAR LIMIT=35] -->
-    <string name="overall_severity_level_critical_safety_warning_title">You are at risk</string>
+    <string name="overall_severity_level_critical_safety_warning_title">Risks found</string>
+
+    <!-- Title for the overall Safety Center status when the user security and privacy signals could potentially put their account at risk [CHAR LIMIT=35] -->
+    <string name="overall_severity_level_account_recommendation_title">Account may be at risk</string>
+
+    <!-- Title for the overall Safety Center status when the user security and privacy signals are putting their account at risk [CHAR LIMIT=35] -->
+    <string name="overall_severity_level_critical_account_warning_title">Account is at risk</string>
 
     <!-- Summary for the overall Safety Center status when the user security and privacy signals contain alerts that could range from minor suggestions to warnings or critical issues. The number of alerts is given as a parameter [CHAR LIMIT=60] -->
     <string name="overall_severity_n_alerts_summary">{count, plural,
diff --git a/SafetyCenter/ResourcesLib/tests/AndroidTest.xml b/SafetyCenter/ResourcesLib/tests/AndroidTest.xml
index 992468e..11d2e75 100644
--- a/SafetyCenter/ResourcesLib/tests/AndroidTest.xml
+++ b/SafetyCenter/ResourcesLib/tests/AndroidTest.xml
@@ -23,7 +23,7 @@
     <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk33ModuleController" />
 
     <!-- Install test and resources -->
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="test-file-name" value="SafetyCenterResourcesLibTests.apk" />
         <option name="test-file-name" value="SafetyCenterResourcesLibTestResources.apk" />
         <option name="cleanup-apks" value="true" />
diff --git a/SafetyCenter/TEST_MAPPING b/SafetyCenter/TEST_MAPPING
index 5feb2e9..c702ee8 100644
--- a/SafetyCenter/TEST_MAPPING
+++ b/SafetyCenter/TEST_MAPPING
@@ -7,11 +7,22 @@
           "exclude-annotation": "com.android.bedstead.harrier.annotations.Postsubmit"
         }
       ]
+    },
+    {
+      "name": "SafetyCenterFunctionalTestCases",
+      "options": [
+        {
+          "exclude-annotation": "com.android.bedstead.harrier.annotations.Postsubmit"
+        }
+      ]
     }
   ],
   "postsubmit": [
     {
       "name": "CtsSafetyCenterTestCases"
+    },
+    {
+      "name": "SafetyCenterFunctionalTestCases"
     }
   ],
   "mainline-presubmit": [
diff --git a/SafetyLabel/Android.bp b/SafetyLabel/Android.bp
new file mode 100644
index 0000000..119890d
--- /dev/null
+++ b/SafetyLabel/Android.bp
@@ -0,0 +1,49 @@
+// Copyright (C) 2022 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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+    name: "safety-label-java-sources",
+    srcs: [
+        "java/**/*.java",
+    ],
+    path: "java",
+    visibility: ["//visibility:private"],
+}
+
+java_library {
+    name: "safety-label",
+    sdk_version: "system_current",
+    min_sdk_version: "30",
+    target_sdk_version: "33",
+    srcs: [
+        ":safety-label-java-sources",
+    ],
+    libs: [
+        "androidx.annotation_annotation",
+        "framework-annotations-lib",
+    ],
+    apex_available: [
+        "com.android.permission",
+        "test_com.android.permission",
+    ],
+    installable: false,
+    visibility: [
+        "//packages/modules/Permission:__subpackages__",
+    ],
+}
+
diff --git a/SafetyLabel/java/com/android/permission/safetylabel/DataCategory.java b/SafetyLabel/java/com/android/permission/safetylabel/DataCategory.java
new file mode 100644
index 0000000..395468c
--- /dev/null
+++ b/SafetyLabel/java/com/android/permission/safetylabel/DataCategory.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permission.safetylabel;
+
+import android.os.PersistableBundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.permission.safetylabel.DataLabelConstants.DataUsage;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Data usage category representation containing one or more {@link DataType}. Valid category keys
+ * are defined in {@link DataCategoryConstants}, each category has a valid set of types {@link
+ * DataType}, which are mapped in {@link DataTypeConstants}
+ */
+public class DataCategory {
+    private final Map<String, DataType> mDataTypes;
+
+    private DataCategory(@NonNull Map<String, DataType> dataTypes) {
+        this.mDataTypes = dataTypes;
+    }
+
+    /**
+     * Returns a {@link java.util.Collections.UnmodifiableMap} of {@link String} category to {@link
+     * DataCategory} created by parsing a {@link PersistableBundle}
+     */
+    @NonNull
+    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    static Map<String, DataCategory> getDataCategoryMap(
+            @Nullable PersistableBundle dataLabelBundle, @DataUsage @NonNull String dataUsage) {
+        if (dataLabelBundle == null) {
+            return Collections.emptyMap();
+        }
+
+        PersistableBundle dataCategoryMapBundle = dataLabelBundle.getPersistableBundle(dataUsage);
+        if (dataCategoryMapBundle == null) {
+            return Collections.emptyMap();
+        }
+
+        Map<String, DataCategory> dataCategoryMap = new HashMap<>();
+        for (String category : DataCategoryConstants.VALID_CATEGORIES) {
+            DataCategory dataCategory = getDataCategory(dataCategoryMapBundle, dataUsage, category);
+            if (dataCategory != null) {
+                dataCategoryMap.put(category, dataCategory);
+            }
+        }
+        return Collections.unmodifiableMap(dataCategoryMap);
+    }
+
+    /**
+     * Returns a {@link DataCategory} created by parsing a {@link PersistableBundle}, or {@code
+     * null} if parsing results in an invalid or empty DataCategory
+     */
+    @Nullable
+    @VisibleForTesting
+    static DataCategory getDataCategory(
+            @Nullable PersistableBundle dataCategoryMapBundle,
+            @NonNull String dataUsage,
+            @NonNull String category) {
+        if (dataCategoryMapBundle == null) {
+            return null;
+        }
+
+        PersistableBundle dataCategoryBundle = dataCategoryMapBundle.getPersistableBundle(category);
+
+        Map<String, DataType> dataTypeMap =
+                DataType.getDataTypeMap(dataCategoryBundle, dataUsage, category);
+        if (dataTypeMap.isEmpty()) {
+            return null;
+        }
+
+        return new DataCategory(dataTypeMap);
+    }
+
+    /** Return the type {@link Map} of String type key to {@link DataType} */
+    @NonNull
+    public Map<String, DataType> getDataTypes() {
+        return mDataTypes;
+    }
+}
diff --git a/SafetyLabel/java/com/android/permission/safetylabel/DataCategoryConstants.java b/SafetyLabel/java/com/android/permission/safetylabel/DataCategoryConstants.java
new file mode 100644
index 0000000..af04220
--- /dev/null
+++ b/SafetyLabel/java/com/android/permission/safetylabel/DataCategoryConstants.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permission.safetylabel;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.StringDef;
+
+import java.lang.annotation.Retention;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Constants for determining valid {@link String} data types for usage within {@link SafetyLabel},
+ * {@link DataCategory}, and {@link DataType}
+ */
+public class DataCategoryConstants {
+
+    /** List of valid Safety Label data collection/sharing categories */
+    @Retention(SOURCE)
+    @StringDef(
+            prefix = "CATEGORY_",
+            value = {
+                CATEGORY_PERSONAL,
+                CATEGORY_FINANCIAL,
+                CATEGORY_LOCATION,
+                CATEGORY_EMAIL_TEXT_MESSAGE,
+                CATEGORY_PHOTO_VIDEO,
+                CATEGORY_AUDIO,
+                CATEGORY_STORAGE,
+                CATEGORY_HEALTH_FITNESS,
+                CATEGORY_CONTACTS,
+                CATEGORY_CALENDAR,
+                CATEGORY_IDENTIFIERS,
+                CATEGORY_APP_PERFORMANCE,
+                CATEGORY_ACTIONS_IN_APP,
+                CATEGORY_SEARCH_AND_BROWSING,
+            })
+    public @interface Category {}
+
+    public static final String CATEGORY_PERSONAL = "personal";
+    public static final String CATEGORY_FINANCIAL = "financial";
+    public static final String CATEGORY_LOCATION = "location";
+    public static final String CATEGORY_EMAIL_TEXT_MESSAGE = "email_text_message";
+    public static final String CATEGORY_PHOTO_VIDEO = "photo_video";
+    public static final String CATEGORY_AUDIO = "audio";
+    public static final String CATEGORY_STORAGE = "storage";
+    public static final String CATEGORY_HEALTH_FITNESS = "health_fitness";
+    public static final String CATEGORY_CONTACTS = "contacts";
+    public static final String CATEGORY_CALENDAR = "calendar";
+    public static final String CATEGORY_IDENTIFIERS = "identifiers";
+    public static final String CATEGORY_APP_PERFORMANCE = "app_performance";
+    public static final String CATEGORY_ACTIONS_IN_APP = "actions_in_app";
+    public static final String CATEGORY_SEARCH_AND_BROWSING = "search_and_browsing";
+
+    /** Set of valid categories */
+    @DataCategoryConstants.Category
+    public static final Set<String> VALID_CATEGORIES =
+            Collections.unmodifiableSet(
+                    new HashSet<>(
+                            Arrays.asList(
+                                    CATEGORY_PERSONAL,
+                                    CATEGORY_FINANCIAL,
+                                    CATEGORY_LOCATION,
+                                    CATEGORY_EMAIL_TEXT_MESSAGE,
+                                    CATEGORY_PHOTO_VIDEO,
+                                    CATEGORY_AUDIO,
+                                    CATEGORY_STORAGE,
+                                    CATEGORY_HEALTH_FITNESS,
+                                    CATEGORY_CONTACTS,
+                                    CATEGORY_CALENDAR,
+                                    CATEGORY_IDENTIFIERS,
+                                    CATEGORY_APP_PERFORMANCE,
+                                    CATEGORY_ACTIONS_IN_APP,
+                                    CATEGORY_SEARCH_AND_BROWSING)));
+
+    /** Returns {@link Set} of valid {@link String} category keys */
+    public static Set<String> getValidDataCategories() {
+        return VALID_CATEGORIES;
+    }
+
+    private DataCategoryConstants() {
+        /* do nothing - hide constructor */
+    }
+}
diff --git a/SafetyLabel/java/com/android/permission/safetylabel/DataLabel.java b/SafetyLabel/java/com/android/permission/safetylabel/DataLabel.java
new file mode 100644
index 0000000..564d547
--- /dev/null
+++ b/SafetyLabel/java/com/android/permission/safetylabel/DataLabel.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permission.safetylabel;
+
+import static com.android.permission.safetylabel.DataLabelConstants.DATA_USAGE_COLLECTED;
+import static com.android.permission.safetylabel.DataLabelConstants.DATA_USAGE_SHARED;
+
+import android.os.PersistableBundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import java.util.Map;
+
+/**
+ * Data label representation with data shared and data collected maps containing zero or more
+ * {@link DataCategory}
+ */
+public class DataLabel {
+    @VisibleForTesting static final String KEY_DATA_LABEL = "data_labels";
+    private final Map<String, DataCategory> mDataCollected;
+    private final Map<String, DataCategory> mDataShared;
+
+    public DataLabel(
+            @NonNull Map<String, DataCategory> dataCollected,
+            @NonNull Map<String, DataCategory> dataShared) {
+        mDataCollected = dataCollected;
+        mDataShared = dataShared;
+    }
+
+    /** Returns a {@link DataLabel} created by parsing a SafetyLabel {@link PersistableBundle} */
+    @NonNull
+    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    public static DataLabel getDataLabel(@Nullable PersistableBundle safetyLabelBundle) {
+        if (safetyLabelBundle == null) {
+            return null;
+        }
+
+        PersistableBundle dataLabelBundle = safetyLabelBundle.getPersistableBundle(KEY_DATA_LABEL);
+        if (dataLabelBundle == null) {
+            return null;
+        }
+
+        Map<String, DataCategory> dataCollectedCategoryMap =
+                DataCategory.getDataCategoryMap(dataLabelBundle, DATA_USAGE_COLLECTED);
+        Map<String, DataCategory> dataSharedCategoryMap =
+                DataCategory.getDataCategoryMap(dataLabelBundle, DATA_USAGE_SHARED);
+        return new DataLabel(dataCollectedCategoryMap, dataSharedCategoryMap);
+    }
+
+    /**
+     * Returns the data collected {@link Map} of {@link
+     * com.android.permission.safetylabel.DataCategoryConstants.Category} to {@link DataCategory}
+     */
+    @NonNull
+    public Map<String, DataCategory> getDataCollected() {
+        return mDataCollected;
+    }
+
+    /**
+     * Returns the data shared {@link Map} of {@link
+     * com.android.permission.safetylabel.DataCategoryConstants.Category} to {@link DataCategory}
+     */
+    @NonNull
+    public Map<String, DataCategory> getDataShared() {
+        return mDataShared;
+    }
+}
diff --git a/SafetyLabel/java/com/android/permission/safetylabel/DataLabelConstants.java b/SafetyLabel/java/com/android/permission/safetylabel/DataLabelConstants.java
new file mode 100644
index 0000000..f592d9b
--- /dev/null
+++ b/SafetyLabel/java/com/android/permission/safetylabel/DataLabelConstants.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permission.safetylabel;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.StringDef;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Constants and util methods for determining valid {@link String} data types for usage within
+ * {@link SafetyLabel} and {@link DataLabel}
+ */
+public class DataLabelConstants {
+
+    /** List of valid Safety Label data usages. Shared vs Collected */
+    @Retention(SOURCE)
+    @StringDef(
+            prefix = "DATA_USAGE_",
+            value = {
+                    DATA_USAGE_COLLECTED,
+                    DATA_USAGE_SHARED,
+            })
+    public @interface DataUsage {}
+    public static final String DATA_USAGE_COLLECTED = "data_collected";
+    public static final String DATA_USAGE_SHARED = "data_shared";
+}
diff --git a/SafetyLabel/java/com/android/permission/safetylabel/DataPurposeConstants.java b/SafetyLabel/java/com/android/permission/safetylabel/DataPurposeConstants.java
new file mode 100644
index 0000000..0e1bb31
--- /dev/null
+++ b/SafetyLabel/java/com/android/permission/safetylabel/DataPurposeConstants.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permission.safetylabel;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Constants for determining valid {@link Integer} data usage purposes for usage within
+ * {@link DataType}
+ */
+public class DataPurposeConstants {
+
+    /** List of valid data usage purposes */
+    @Retention(SOURCE)
+    @IntDef(
+            prefix = "PURPOSE_",
+            value = {
+                PURPOSE_APP_FUNCTIONALITY,
+                PURPOSE_ANALYTICS,
+                PURPOSE_DEVELOPER_COMMUNICATIONS,
+                PURPOSE_FRAUD_PREVENTION_SECURITY,
+                PURPOSE_ADVERTISING,
+                PURPOSE_PERSONALIZATION,
+                PURPOSE_ACCOUNT_MANAGEMENT,
+            })
+    public @interface Purpose {}
+
+    public static final int PURPOSE_APP_FUNCTIONALITY = 1;
+    public static final int PURPOSE_ANALYTICS = 2;
+    public static final int PURPOSE_DEVELOPER_COMMUNICATIONS = 3;
+    public static final int PURPOSE_FRAUD_PREVENTION_SECURITY = 4;
+    public static final int PURPOSE_ADVERTISING = 5;
+    public static final int PURPOSE_PERSONALIZATION = 6;
+    public static final int PURPOSE_ACCOUNT_MANAGEMENT = 7;
+    // RESERVED/DEPRECATED  = 8
+    // RESERVED/DEPRECATED  = 9
+
+    /** {@link Set} of valid {@link Integer} purposes */
+    @Purpose
+    public  static final Set<Integer> VALID_PURPOSES =
+            Collections.unmodifiableSet(
+                    new HashSet<>(
+                            Arrays.asList(
+                                    PURPOSE_APP_FUNCTIONALITY,
+                                    PURPOSE_ANALYTICS,
+                                    PURPOSE_DEVELOPER_COMMUNICATIONS,
+                                    PURPOSE_FRAUD_PREVENTION_SECURITY,
+                                    PURPOSE_ADVERTISING,
+                                    PURPOSE_PERSONALIZATION,
+                                    PURPOSE_ACCOUNT_MANAGEMENT)));
+
+    /** Returns {@link Set} of valid {@link Integer} purpose */
+    @Purpose
+    public static Set<Integer> getValidPurposes() {
+        return VALID_PURPOSES;
+    }
+}
diff --git a/SafetyLabel/java/com/android/permission/safetylabel/DataType.java b/SafetyLabel/java/com/android/permission/safetylabel/DataType.java
new file mode 100644
index 0000000..cc63b07
--- /dev/null
+++ b/SafetyLabel/java/com/android/permission/safetylabel/DataType.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permission.safetylabel;
+
+import android.os.PersistableBundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.permission.safetylabel.DataPurposeConstants.Purpose;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Data usage type representation. Types are specific to a {@link DataCategory} and contains
+ * metadata related to the data usage purpose.
+ */
+public class DataType {
+    @VisibleForTesting static final String KEY_PURPOSES = "purposes";
+    @VisibleForTesting static final String KEY_USER_CONTROL = "user_control";
+    @VisibleForTesting static final String KEY_EPHEMERAL = "ephemeral";
+
+    @Purpose private final Set<Integer> mPurposeSet;
+    private final Boolean mUserControl;
+    private final Boolean mEphemeral;
+
+    private DataType(
+            @NonNull @Purpose Set<Integer> purposeSet,
+            @Nullable Boolean userControl,
+            @Nullable Boolean ephemeral) {
+        this.mPurposeSet = purposeSet;
+        this.mUserControl = userControl;
+        this.mEphemeral = ephemeral;
+    }
+
+    /**
+     * Returns a {@link java.util.Collections.UnmodifiableMap} of String type key to {@link
+     * DataType} created by parsing a {@link PersistableBundle}
+     */
+    @NonNull
+    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    static Map<String, DataType> getDataTypeMap(
+            @Nullable PersistableBundle dataCategoryBundle,
+            @NonNull String dataUsage,
+            @NonNull String category) {
+        if (dataCategoryBundle == null || dataCategoryBundle.isEmpty()) {
+            return Collections.emptyMap();
+        }
+
+        Map<String, DataType> dataTypeMap = new HashMap<>();
+        Set<String> validDataTypesForCategory =
+                DataTypeConstants.getValidDataTypesForCategory(category);
+        for (String type : validDataTypesForCategory) {
+            PersistableBundle dataTypeBundle = dataCategoryBundle.getPersistableBundle(type);
+            DataType dataType = getDataType(dataTypeBundle, dataUsage);
+            if (dataType != null) {
+                dataTypeMap.put(type, dataType);
+            }
+        }
+        return Collections.unmodifiableMap(dataTypeMap);
+    }
+
+    /**
+     * Returns {@link DataType} created by parsing the {@link android.os.PersistableBundle}
+     * representation of DataType
+     */
+    @Nullable
+    @VisibleForTesting
+    static DataType getDataType(
+            @Nullable PersistableBundle dataTypeBundle, @NonNull String dataUsage) {
+        if (dataTypeBundle == null || dataTypeBundle.isEmpty()) {
+            return null;
+        }
+
+        // purposes are required, if not present, treat as invalid
+        int[] purposeList = dataTypeBundle.getIntArray(KEY_PURPOSES);
+        if (purposeList == null || purposeList.length == 0) {
+            return null;
+        }
+
+        // Filter to set of valid purposes, and return invalid if empty
+        Set<Integer> purposeSet = new HashSet<>();
+        for (int purpose : purposeList) {
+            if (DataPurposeConstants.getValidPurposes().contains(purpose)) {
+                purposeSet.add(purpose);
+            }
+        }
+        if (purposeSet.isEmpty()) {
+            return null;
+        }
+
+        // Only set/expected for DATA COLLECTED. DATA SHARED should be null
+        Boolean userControl = null;
+        Boolean ephemeral = null;
+        if (DataLabelConstants.DATA_USAGE_COLLECTED.equals(dataUsage)) {
+            userControl =
+                    dataTypeBundle.containsKey(KEY_USER_CONTROL)
+                            ? dataTypeBundle.getBoolean(KEY_USER_CONTROL)
+                            : null;
+            ephemeral =
+                    dataTypeBundle.containsKey(KEY_EPHEMERAL)
+                            ? dataTypeBundle.getBoolean(KEY_EPHEMERAL)
+                            : null;
+        }
+
+        return new DataType(purposeSet, userControl, ephemeral);
+    }
+
+    /**
+     * Returns {@link Set} of valid {@link Integer} purposes for using the associated data category
+     * and type
+     */
+    @NonNull
+    public Set<Integer> getPurposeSet() {
+        return mPurposeSet;
+    }
+
+    /**
+     * For data-collected, returns {@code true} if data usage is user optional and {@code false} if
+     * data usage is required. Should return {@code null} for data-shared.
+     */
+    @Nullable
+    public Boolean getUserControl() {
+        return mUserControl;
+    }
+
+    /**
+     * For data-collected, returns {@code true} if data usage is user optional and {@code false} if
+     * data usage is processed ephemerally. Should return {@code null} for data-shared.
+     */
+    @Nullable
+    public Boolean getEphemeral() {
+        return mEphemeral;
+    }
+}
diff --git a/SafetyLabel/java/com/android/permission/safetylabel/DataTypeConstants.java b/SafetyLabel/java/com/android/permission/safetylabel/DataTypeConstants.java
new file mode 100644
index 0000000..1b4819c
--- /dev/null
+++ b/SafetyLabel/java/com/android/permission/safetylabel/DataTypeConstants.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permission.safetylabel;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.StringDef;
+import android.util.ArrayMap;
+
+import java.lang.annotation.Retention;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Constants and util methods for determining valid {@link String} data categories for usage within
+ * {@link SafetyLabel}, {@link DataCategory}, and {@link DataType}
+ */
+public class DataTypeConstants {
+
+    /**
+     * List of valid Safety Label data collection/sharing types for {@link
+     * DataCategoryConstants#CATEGORY_PERSONAL}
+     */
+    @Retention(SOURCE)
+    @StringDef(
+            prefix = "PERSONAL_",
+            value = {
+                PERSONAL_NAME,
+                PERSONAL_EMAIL_ADDRESS,
+                PERSONAL_PHYSICAL_ADDRESS,
+                PERSONAL_PHONE_NUMBER,
+                PERSONAL_RACE_ETHNICITY,
+                PERSONAL_POLITICAL_OR_RELIGIOUS_BELIEFS,
+                PERSONAL_SEXUAL_ORIENTATION_OR_GENDER_IDENTITY,
+                PERSONAL_IDENTIFIERS,
+                PERSONAL_OTHER,
+            })
+    public @interface PersonalType {}
+
+    public static final String PERSONAL_NAME = "NAME";
+    public static final String PERSONAL_EMAIL_ADDRESS = "EMAIL_ADDRESS";
+    public static final String PERSONAL_PHYSICAL_ADDRESS = "PHYSICAL_ADDRESS";
+    public static final String PERSONAL_PHONE_NUMBER = "PHONE_NUMBER";
+    public static final String PERSONAL_RACE_ETHNICITY = "RACE_ETHNICITY";
+    public static final String PERSONAL_POLITICAL_OR_RELIGIOUS_BELIEFS =
+            "POLITICAL_OR_RELIGIOUS_BELIEFS";
+    public static final String PERSONAL_SEXUAL_ORIENTATION_OR_GENDER_IDENTITY =
+            "SEXUAL_ORIENTATION_OR_GENDER_IDENTITY";
+    public static final String PERSONAL_IDENTIFIERS = "PERSONAL_IDENTIFIERS";
+    public static final String PERSONAL_OTHER = "OTHER";
+
+    @PersonalType
+    private static final Set<String> VALID_TYPES_PERSONAL =
+            Collections.unmodifiableSet(
+                    new HashSet<>(
+                            Arrays.asList(
+                                    PERSONAL_NAME,
+                                    PERSONAL_EMAIL_ADDRESS,
+                                    PERSONAL_PHYSICAL_ADDRESS,
+                                    PERSONAL_PHONE_NUMBER,
+                                    PERSONAL_RACE_ETHNICITY,
+                                    PERSONAL_POLITICAL_OR_RELIGIOUS_BELIEFS,
+                                    PERSONAL_SEXUAL_ORIENTATION_OR_GENDER_IDENTITY,
+                                    PERSONAL_IDENTIFIERS,
+                                    PERSONAL_OTHER)));
+
+    /**
+     * List of valid Safety Label data collection/sharing types for {@link
+     * DataCategoryConstants#CATEGORY_FINANCIAL}
+     */
+    @Retention(SOURCE)
+    @StringDef(
+            prefix = "FINANCIAL_",
+            value = {
+                FINANCIAL_CARD_BANK_ACCOUNT,
+                FINANCIAL_PURCHASE_HISTORY,
+                FINANCIAL_CREDIT_SCORE,
+                FINANCIAL_OTHER,
+            })
+    public @interface FinancialType {}
+
+    public static final String FINANCIAL_CARD_BANK_ACCOUNT = "CARD_BANK_ACCOUNT";
+    public static final String FINANCIAL_PURCHASE_HISTORY = "PURCHASE_HISTORY";
+    public static final String FINANCIAL_CREDIT_SCORE = "CREDIT_SCORE";
+    public static final String FINANCIAL_OTHER = "OTHER";
+
+    @FinancialType
+    private static final Set<String> VALID_TYPES_FINANCIAL =
+            Collections.unmodifiableSet(
+                    new HashSet<>(
+                            Arrays.asList(
+                                    FINANCIAL_CARD_BANK_ACCOUNT,
+                                    FINANCIAL_PURCHASE_HISTORY,
+                                    FINANCIAL_CREDIT_SCORE,
+                                    FINANCIAL_OTHER)));
+
+    /**
+     * List of valid Safety Label data collection/sharing types for {@link
+     * DataCategoryConstants#CATEGORY_LOCATION}
+     */
+    @Retention(SOURCE)
+    @StringDef(
+            prefix = "LOCATION_",
+            value = {
+                LOCATION_APPROX_LOCATION,
+                LOCATION_PRECISE_LOCATION,
+            })
+    public @interface LocationType {}
+
+    public static final String LOCATION_APPROX_LOCATION = "APPROX_LOCATION";
+    public static final String LOCATION_PRECISE_LOCATION = "PRECISE_LOCATION";
+
+    @LocationType
+    private static final Set<String> VALID_TYPES_LOCATION =
+            Collections.unmodifiableSet(
+                    new HashSet<>(
+                            Arrays.asList(LOCATION_APPROX_LOCATION, LOCATION_PRECISE_LOCATION)));
+
+    /**
+     * List of valid Safety Label data collection/sharing types for {@link
+     * DataCategoryConstants#CATEGORY_EMAIL_TEXT_MESSAGE}
+     */
+    @Retention(SOURCE)
+    @StringDef(
+            prefix = "EMAIL_TEXT_MESSAGE_",
+            value = {
+                EMAIL_TEXT_MESSAGE_EMAILS,
+                EMAIL_TEXT_MESSAGE_TEXT_MESSAGES,
+                EMAIL_TEXT_MESSAGE_OTHER,
+            })
+    public @interface EmailTextMessageType {}
+
+    public static final String EMAIL_TEXT_MESSAGE_EMAILS = "EMAILS";
+    public static final String EMAIL_TEXT_MESSAGE_TEXT_MESSAGES = "TEXT_MESSAGES";
+    public static final String EMAIL_TEXT_MESSAGE_OTHER = "OTHER";
+
+    @EmailTextMessageType
+    private static final Set<String> VALID_TYPES_EMAIL_TEXT_MESSAGE =
+            Collections.unmodifiableSet(
+                    new HashSet<>(
+                            Arrays.asList(
+                                    EMAIL_TEXT_MESSAGE_EMAILS,
+                                    EMAIL_TEXT_MESSAGE_TEXT_MESSAGES,
+                                    EMAIL_TEXT_MESSAGE_OTHER)));
+
+    /**
+     * List of valid Safety Label data collection/sharing types for {@link
+     * DataCategoryConstants#CATEGORY_PHOTO_VIDEO}
+     */
+    @Retention(SOURCE)
+    @StringDef(
+            prefix = "PHOTO_VIDEO_",
+            value = {
+                PHOTO_VIDEO_PHOTOS,
+                PHOTO_VIDEO_VIDEOS,
+            })
+    public @interface PhotoVideoType {}
+
+    public static final String PHOTO_VIDEO_PHOTOS = "PHOTOS";
+    public static final String PHOTO_VIDEO_VIDEOS = "VIDEOS";
+
+    @PhotoVideoType
+    private static final Set<String> VALID_TYPES_PHOTO_VIDEO =
+            Collections.unmodifiableSet(
+                    new HashSet<>(Arrays.asList(PHOTO_VIDEO_PHOTOS, PHOTO_VIDEO_VIDEOS)));
+
+    /**
+     * List of valid Safety Label data collection/sharing types for {@link
+     * DataCategoryConstants#CATEGORY_AUDIO}
+     */
+    @Retention(SOURCE)
+    @StringDef(
+            prefix = "AUDIO_",
+            value = {AUDIO_SOUND_RECORDINGS, AUDIO_MUSIC_FILES, AUDIO_OTHER})
+    public @interface AudioType {}
+
+    public static final String AUDIO_SOUND_RECORDINGS = "SOUND_RECORDINGS";
+    public static final String AUDIO_MUSIC_FILES = "MUSIC_FILES";
+    public static final String AUDIO_OTHER = "OTHER";
+
+    @AudioType
+    private static final Set<String> VALID_TYPES_AUDIO =
+            Collections.unmodifiableSet(
+                    new HashSet<>(
+                            Arrays.asList(AUDIO_SOUND_RECORDINGS, AUDIO_MUSIC_FILES, AUDIO_OTHER)));
+
+    /**
+     * List of valid Safety Label data collection/sharing types for {@link
+     * DataCategoryConstants#CATEGORY_STORAGE}
+     */
+    @Retention(SOURCE)
+    @StringDef(
+            prefix = "STORAGE_",
+            value = {
+                STORAGE_FILES_DOCS,
+            })
+    public @interface StorageType {}
+
+    public static final String STORAGE_FILES_DOCS = "FILES_DOCS";
+
+    @StorageType
+    private static final Set<String> VALID_TYPES_STORAGE =
+            Collections.unmodifiableSet(new HashSet<>(Arrays.asList(STORAGE_FILES_DOCS)));
+
+    /**
+     * List of valid Safety Label data collection/sharing types for {@link
+     * DataCategoryConstants#CATEGORY_HEALTH_FITNESS}
+     */
+    @Retention(SOURCE)
+    @StringDef(
+            prefix = "HEALTH_FITNESS_",
+            value = {
+                HEALTH_FITNESS_HEALTH,
+                HEALTH_FITNESS_FITNESS,
+            })
+    public @interface HealthFitnessType {}
+
+    public static final String HEALTH_FITNESS_HEALTH = "HEALTH";
+    public static final String HEALTH_FITNESS_FITNESS = "FITNESS";
+
+    @HealthFitnessType
+    private static final Set<String> VALID_TYPES_HEALTH_FITNESS =
+            Collections.unmodifiableSet(
+                    new HashSet<>(Arrays.asList(HEALTH_FITNESS_HEALTH, HEALTH_FITNESS_FITNESS)));
+
+    /**
+     * List of valid Safety Label data collection/sharing types for {@link
+     * DataCategoryConstants#CATEGORY_CONTACTS}
+     */
+    @Retention(SOURCE)
+    @StringDef(
+            prefix = "CONTACTS_",
+            value = {
+                CONTACTS_CONTACTS,
+            })
+    public @interface ContactsType {}
+
+    public static final String CONTACTS_CONTACTS = "CONTACTS";
+
+    @ContactsType
+    private static final Set<String> VALID_TYPES_CONTACTS =
+            Collections.unmodifiableSet(new HashSet<>(Arrays.asList(CONTACTS_CONTACTS)));
+
+    /**
+     * List of valid Safety Label data collection/sharing types for {@link
+     * DataCategoryConstants#CATEGORY_CALENDAR}
+     */
+    @Retention(SOURCE)
+    @StringDef(
+            prefix = "CALENDAR_",
+            value = {
+                CALENDAR_CALENDAR,
+            })
+    public @interface CalendarType {}
+
+    public static final String CALENDAR_CALENDAR = "CALENDAR";
+
+    @CalendarType
+    private static final Set<String> VALID_TYPES_CALENDAR =
+            Collections.unmodifiableSet(new HashSet<>(Arrays.asList(CALENDAR_CALENDAR)));
+
+    /**
+     * List of valid Safety Label data collection/sharing types for {@link
+     * DataCategoryConstants#CATEGORY_IDENTIFIERS}
+     */
+    @Retention(SOURCE)
+    @StringDef(
+            prefix = "IDENTIFIERS_",
+            value = {
+                IDENTIFIERS_OTHER,
+            })
+    public @interface IdentifiersType {}
+
+    public static final String IDENTIFIERS_OTHER = "OTHER";
+
+    @IdentifiersType
+    private static final Set<String> VALID_TYPES_IDENTIFIERS =
+            Collections.unmodifiableSet(new HashSet<>(Arrays.asList(IDENTIFIERS_OTHER)));
+
+    /**
+     * List of valid Safety Label data collection/sharing types for {@link
+     * DataCategoryConstants#CATEGORY_APP_PERFORMANCE}
+     */
+    @Retention(SOURCE)
+    @StringDef(
+            prefix = "APP_PERFORMANCE_",
+            value = {
+                APP_PERFORMANCE_CRASH_LOGS,
+                APP_PERFORMANCE_PERFORMANCE_DIAGNOSTICS,
+                APP_PERFORMANCE_OTHER,
+            })
+    public @interface AppPerformanceType {}
+
+    public static final String APP_PERFORMANCE_CRASH_LOGS = "CRASH_LOGS";
+    public static final String APP_PERFORMANCE_PERFORMANCE_DIAGNOSTICS = "PERFORMANCE_DIAGNOSTICS";
+    public static final String APP_PERFORMANCE_OTHER = "OTHER";
+
+    @AppPerformanceType
+    private static final Set<String> VALID_TYPES_APP_PERFORMANCE =
+            Collections.unmodifiableSet(
+                    new HashSet<>(
+                            Arrays.asList(
+                                    APP_PERFORMANCE_CRASH_LOGS,
+                                    APP_PERFORMANCE_PERFORMANCE_DIAGNOSTICS,
+                                    APP_PERFORMANCE_OTHER)));
+
+    /**
+     * List of valid Safety Label data collection/sharing types for {@link
+     * DataCategoryConstants#CATEGORY_ACTIONS_IN_APP}
+     */
+    @Retention(SOURCE)
+    @StringDef(
+            prefix = "ACTIONS_IN_APP_",
+            value = {
+                ACTIONS_IN_APP_USER_INTERACTION,
+                ACTIONS_IN_APP_IN_APP_SEARCH_HISTORY,
+                ACTIONS_IN_APP_INSTALLED_APPS,
+                ACTIONS_IN_APP_USER_GENERATED_CONTENT,
+                ACTIONS_IN_APP_OTHER,
+            })
+    public @interface ActionsInAppType {}
+
+    public static final String ACTIONS_IN_APP_USER_INTERACTION = "USER_INTERACTION";
+    public static final String ACTIONS_IN_APP_IN_APP_SEARCH_HISTORY = "IN_APP_SEARCH_HISTORY";
+    public static final String ACTIONS_IN_APP_INSTALLED_APPS = "INSTALLED_APPS";
+    public static final String ACTIONS_IN_APP_USER_GENERATED_CONTENT = "USER_GENERATED_CONTENT";
+    public static final String ACTIONS_IN_APP_OTHER = "OTHER";
+
+    @ActionsInAppType
+    private static final Set<String> VALID_TYPES_ACTIONS_IN_APP =
+            Collections.unmodifiableSet(
+                    new HashSet<>(
+                            Arrays.asList(
+                                    ACTIONS_IN_APP_USER_INTERACTION,
+                                    ACTIONS_IN_APP_IN_APP_SEARCH_HISTORY,
+                                    ACTIONS_IN_APP_INSTALLED_APPS,
+                                    ACTIONS_IN_APP_USER_GENERATED_CONTENT,
+                                    ACTIONS_IN_APP_OTHER)));
+
+    /**
+     * List of valid Safety Label data collection/sharing types for {@link
+     * DataCategoryConstants#CATEGORY_SEARCH_AND_BROWSING}
+     */
+    @Retention(SOURCE)
+    @StringDef(
+            prefix = "SEARCH_AND_BROWSING_",
+            value = {
+                SEARCH_AND_BROWSING_WEB_BROWSING_HISTORY,
+            })
+    public @interface SearchAndBrowsingType {}
+
+    public static final String SEARCH_AND_BROWSING_WEB_BROWSING_HISTORY = "WEB_BROWSING_HISTORY";
+
+    @SearchAndBrowsingType
+    private static final Set<String> VALID_TYPES_SEARCH_AND_BROWSING =
+            Collections.unmodifiableSet(
+                    new HashSet<>(Arrays.asList(SEARCH_AND_BROWSING_WEB_BROWSING_HISTORY)));
+
+    private static final Map<String, Set<String>> VALID_TYPES_FOR_CATEGORY_MAP;
+
+    /** Returns {@link Set} of valid types for the specified {@link String} category key */
+    public static Set<String> getValidDataTypesForCategory(
+            @DataCategoryConstants.Category String category) {
+        return VALID_TYPES_FOR_CATEGORY_MAP.containsKey(category)
+                ? VALID_TYPES_FOR_CATEGORY_MAP.get(category)
+                : Collections.emptySet();
+    }
+
+    static {
+        VALID_TYPES_FOR_CATEGORY_MAP = new ArrayMap<>();
+        VALID_TYPES_FOR_CATEGORY_MAP.put(
+                DataCategoryConstants.CATEGORY_PERSONAL, VALID_TYPES_PERSONAL);
+        VALID_TYPES_FOR_CATEGORY_MAP.put(
+                DataCategoryConstants.CATEGORY_FINANCIAL, VALID_TYPES_FINANCIAL);
+        VALID_TYPES_FOR_CATEGORY_MAP.put(
+                DataCategoryConstants.CATEGORY_LOCATION, VALID_TYPES_LOCATION);
+        VALID_TYPES_FOR_CATEGORY_MAP.put(
+                DataCategoryConstants.CATEGORY_EMAIL_TEXT_MESSAGE, VALID_TYPES_EMAIL_TEXT_MESSAGE);
+        VALID_TYPES_FOR_CATEGORY_MAP.put(
+                DataCategoryConstants.CATEGORY_PHOTO_VIDEO, VALID_TYPES_PHOTO_VIDEO);
+        VALID_TYPES_FOR_CATEGORY_MAP.put(DataCategoryConstants.CATEGORY_AUDIO, VALID_TYPES_AUDIO);
+        VALID_TYPES_FOR_CATEGORY_MAP.put(
+                DataCategoryConstants.CATEGORY_STORAGE, VALID_TYPES_STORAGE);
+        VALID_TYPES_FOR_CATEGORY_MAP.put(
+                DataCategoryConstants.CATEGORY_HEALTH_FITNESS, VALID_TYPES_HEALTH_FITNESS);
+        VALID_TYPES_FOR_CATEGORY_MAP.put(
+                DataCategoryConstants.CATEGORY_CONTACTS, VALID_TYPES_CONTACTS);
+        VALID_TYPES_FOR_CATEGORY_MAP.put(
+                DataCategoryConstants.CATEGORY_CALENDAR, VALID_TYPES_CALENDAR);
+        VALID_TYPES_FOR_CATEGORY_MAP.put(
+                DataCategoryConstants.CATEGORY_IDENTIFIERS, VALID_TYPES_IDENTIFIERS);
+        VALID_TYPES_FOR_CATEGORY_MAP.put(
+                DataCategoryConstants.CATEGORY_APP_PERFORMANCE, VALID_TYPES_APP_PERFORMANCE);
+        VALID_TYPES_FOR_CATEGORY_MAP.put(
+                DataCategoryConstants.CATEGORY_ACTIONS_IN_APP, VALID_TYPES_ACTIONS_IN_APP);
+        VALID_TYPES_FOR_CATEGORY_MAP.put(
+                DataCategoryConstants.CATEGORY_SEARCH_AND_BROWSING,
+                VALID_TYPES_SEARCH_AND_BROWSING);
+    }
+
+    private DataTypeConstants() {
+        /* do nothing - hide constructor */
+    }
+}
diff --git a/SafetyLabel/java/com/android/permission/safetylabel/SafetyLabel.java b/SafetyLabel/java/com/android/permission/safetylabel/SafetyLabel.java
new file mode 100644
index 0000000..822e9c4
--- /dev/null
+++ b/SafetyLabel/java/com/android/permission/safetylabel/SafetyLabel.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permission.safetylabel;
+
+import android.os.PersistableBundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+/** Safety Label representation containing zero or more {@link DataCategory} for data shared */
+public class SafetyLabel {
+    @VisibleForTesting static final String KEY_SAFETY_LABEL = "safety_labels";
+    private final DataLabel mDataLabel;
+
+    private SafetyLabel(@NonNull DataLabel dataLabel) {
+        this.mDataLabel = dataLabel;
+    }
+
+    /** Returns {@link SafetyLabel} created by parsing a metadata {@link PersistableBundle} */
+    @Nullable
+    public static SafetyLabel getSafetyLabelFromMetadata(@Nullable PersistableBundle bundle) {
+        // TODO(b/261069412): add versioning and nonnull empty/invalid cases
+        if (bundle == null) {
+            return null;
+        }
+
+        PersistableBundle safetyLabelBundle = bundle.getPersistableBundle(KEY_SAFETY_LABEL);
+        if (safetyLabelBundle == null) {
+            return null;
+        }
+
+        DataLabel dataLabel = DataLabel.getDataLabel(safetyLabelBundle);
+        if (dataLabel == null) {
+            return null;
+        }
+
+        return new SafetyLabel(dataLabel);
+    }
+
+    /** Returns the data label for the safety label */
+    @NonNull
+    public DataLabel getDataLabel() {
+        return mDataLabel;
+    }
+}
diff --git a/SafetyLabel/tests/Android.bp b/SafetyLabel/tests/Android.bp
new file mode 100644
index 0000000..2026a6a
--- /dev/null
+++ b/SafetyLabel/tests/Android.bp
@@ -0,0 +1,38 @@
+// Copyright (C) 2022 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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "SafetyLabelTests",
+    defaults: ["mts-target-sdk-version-current"],
+    sdk_version: "test_current",
+    min_sdk_version: "30",
+    target_sdk_version: "32",
+    srcs: [
+        "java/**/*.kt",
+    ],
+    per_testcase_directory: true,
+    static_libs: [
+        "compatibility-device-util-axt",
+        "kotlinx-coroutines-android",
+        "safety-label",
+    ],
+    test_suites: [
+        "general-tests",
+        "mts-permission",
+    ],
+}
diff --git a/SafetyLabel/tests/AndroidManifest.xml b/SafetyLabel/tests/AndroidManifest.xml
new file mode 100644
index 0000000..4d1e629
--- /dev/null
+++ b/SafetyLabel/tests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.permission.safetylabel.tests">
+
+    <application android:label="Safety Label Tests">
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.permission.safetylabel.tests"
+                     android:label="Tests for the Safety Label library"/>
+</manifest>
diff --git a/SafetyLabel/tests/AndroidTest.xml b/SafetyLabel/tests/AndroidTest.xml
new file mode 100644
index 0000000..919e7ce
--- /dev/null
+++ b/SafetyLabel/tests/AndroidTest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+
+<configuration description="Runs unit tests for the Safety Label library.">
+    <option name="test-tag" value="SafetyLabelTests"/>
+    <object type="module_controller"
+            class="com.android.tradefed.testtype.suite.module.Sdk33ModuleController"/>
+
+    <!-- Install test -->
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="SafetyLabelTests.apk"/>
+        <option name="cleanup-apks" value="true"/>
+    </target_preparer>
+
+    <!-- Create place to store apks -->
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="mkdir -p /data/local/tmp/safetylabel/tests/"/>
+        <option name="teardown-command" value="rm -rf /data/local/tmp/safetylabel/"/>
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.permission.safetylabel.tests"/>
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
+    </test>
+</configuration>
diff --git a/SafetyLabel/tests/java/com/android/permission/safetylabel/DataCategoryTest.kt b/SafetyLabel/tests/java/com/android/permission/safetylabel/DataCategoryTest.kt
new file mode 100644
index 0000000..8ed7050
--- /dev/null
+++ b/SafetyLabel/tests/java/com/android/permission/safetylabel/DataCategoryTest.kt
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permission.safetylabel
+
+import android.os.PersistableBundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.permission.safetylabel.DataCategoryConstants.CATEGORY_LOCATION
+import com.android.permission.safetylabel.DataCategoryConstants.VALID_CATEGORIES
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.INVALID_KEY
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createCategoryMapPersistableBundle
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createCategoryMapPersistableBundleWithInvalidTypes
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createDataLabelPersistableBundle
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createDataLabelPersistableBundleWithAdditonalInvalidCategory
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createInvalidCategoryMapPersistableBundle
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createInvalidDataLabelPersistableBundle
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createTypePersistableBundle
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** CTS tests for [DataCategory]. */
+@RunWith(AndroidJUnit4::class)
+class DataCategoryTest {
+    @Test
+    fun getDataCategoryMap_dataUsageCollected_nullPersistableBundle_emptyMap() {
+        val dataCategoryMap =
+            DataCategory.getDataCategoryMap(null, DataLabelConstants.DATA_USAGE_COLLECTED)
+
+        assertThat(dataCategoryMap).isNotNull()
+        assertThat(dataCategoryMap).isEmpty()
+    }
+
+    @Test
+    fun getDataCategoryMap_dataUsageCollected_emptyPersistableBundle_emptyMap() {
+        val dataCategoryMap =
+            DataCategory.getDataCategoryMap(
+                PersistableBundle.EMPTY, DataLabelConstants.DATA_USAGE_COLLECTED)
+
+        assertThat(dataCategoryMap).isNotNull()
+        assertThat(dataCategoryMap).isEmpty()
+    }
+
+    @Test
+    fun getDataCategoryMap_dataUsageCollected_invalidBundle_emptyMap() {
+        val dataCategoryMap =
+            DataCategory.getDataCategoryMap(
+                createInvalidDataLabelPersistableBundle(), DataLabelConstants.DATA_USAGE_COLLECTED)
+
+        assertThat(dataCategoryMap).isNotNull()
+        assertThat(dataCategoryMap).isEmpty()
+    }
+
+    @Test
+    fun getDataCategoryMap_dataUsageCollected_validBundle_hasAllValidCategories() {
+        val dataCategoryMap =
+            DataCategory.getDataCategoryMap(
+                createDataLabelPersistableBundle(), DataLabelConstants.DATA_USAGE_COLLECTED)
+
+        assertThat(dataCategoryMap).isNotNull()
+        assertThat(dataCategoryMap.keys).containsExactlyElementsIn(VALID_CATEGORIES)
+    }
+
+    @Test
+    fun getDataCategoryMap_dataUsageCollected_additionalInvalidCategory_hasOnlyValidCategories() {
+        // Create valid data label and put an additional invalid/unknown category key
+        val dataLabelBundle = createDataLabelPersistableBundleWithAdditonalInvalidCategory()
+
+        val dataCategoryMap =
+            DataCategory.getDataCategoryMap(
+                dataLabelBundle, DataLabelConstants.DATA_USAGE_COLLECTED)
+
+        assertThat(dataCategoryMap).isNotNull()
+        assertThat(dataCategoryMap.keys).containsExactlyElementsIn(VALID_CATEGORIES)
+    }
+
+    @Test
+    fun getDataCategoryMap_dataUsageShared_nullPersistableBundle_emptyMap() {
+        val dataCategoryMap =
+            DataCategory.getDataCategoryMap(null, DataLabelConstants.DATA_USAGE_SHARED)
+
+        assertThat(dataCategoryMap).isNotNull()
+        assertThat(dataCategoryMap).isEmpty()
+    }
+
+    @Test
+    fun getDataCategoryMap_dataUsageShared_emptyPersistableBundle_emptyMap() {
+        val dataCategoryMap =
+            DataCategory.getDataCategoryMap(
+                PersistableBundle.EMPTY, DataLabelConstants.DATA_USAGE_SHARED)
+
+        assertThat(dataCategoryMap).isNotNull()
+        assertThat(dataCategoryMap).isEmpty()
+    }
+
+    @Test
+    fun getDataCategoryMap_dataUsageShared_invalidBundle_emptyMap() {
+        val dataCategoryMap =
+            DataCategory.getDataCategoryMap(
+                createInvalidDataLabelPersistableBundle(), DataLabelConstants.DATA_USAGE_SHARED)
+
+        assertThat(dataCategoryMap).isNotNull()
+        assertThat(dataCategoryMap).isEmpty()
+    }
+
+    @Test
+    fun getDataCategoryMap_dataUsageShared_validBundle_hasAllValidCategories() {
+        val dataCategoryMap =
+            DataCategory.getDataCategoryMap(
+                createDataLabelPersistableBundle(), DataLabelConstants.DATA_USAGE_SHARED)
+
+        assertThat(dataCategoryMap).isNotNull()
+        assertThat(dataCategoryMap.keys).containsExactlyElementsIn(VALID_CATEGORIES)
+    }
+
+    @Test
+    fun getDataCategoryMap_dataUsageShared_additionalInvalidCategory_hasOnlyValidCategories() {
+        // Create valid data label and put an additional invalid/unknown category key
+        val dataLabelBundle = createDataLabelPersistableBundleWithAdditonalInvalidCategory()
+
+        val dataCategoryMap =
+            DataCategory.getDataCategoryMap(dataLabelBundle, DataLabelConstants.DATA_USAGE_SHARED)
+
+        assertThat(dataCategoryMap).isNotNull()
+        assertThat(dataCategoryMap.keys).containsExactlyElementsIn(VALID_CATEGORIES)
+    }
+
+    @Test
+    fun getDataCategoryMap_dataUsageInvalid_nullPersistableBundle_emptyMap() {
+        val dataCategoryMap = DataCategory.getDataCategoryMap(null, "invalid_datausage_key")
+
+        assertThat(dataCategoryMap).isNotNull()
+        assertThat(dataCategoryMap).isEmpty()
+    }
+
+    @Test
+    fun getDataCategoryMap_dataUsageInvalid_emptyPersistableBundle_emptyMap() {
+        val dataCategoryMap =
+            DataCategory.getDataCategoryMap(PersistableBundle.EMPTY, "invalid_datausage_key")
+
+        assertThat(dataCategoryMap).isNotNull()
+        assertThat(dataCategoryMap).isEmpty()
+    }
+
+    @Test
+    fun getDataCategoryMap_dataUsageInvalid_invalidBundle_emptyMap() {
+        val dataCategoryMap =
+            DataCategory.getDataCategoryMap(
+                createInvalidDataLabelPersistableBundle(), "invalid_datausage_key")
+
+        assertThat(dataCategoryMap).isNotNull()
+        assertThat(dataCategoryMap).isEmpty()
+    }
+
+    @Test
+    fun getDataCategoryMap_dataUsageInvalid_validBundle_emptyMap() {
+        val dataCategoryMap =
+            DataCategory.getDataCategoryMap(
+                createDataLabelPersistableBundle(), "invalid_datausage_key")
+
+        assertThat(dataCategoryMap).isNotNull()
+        assertThat(dataCategoryMap).isEmpty()
+    }
+
+    @Test
+    fun getDataCategoryMap_dataUsageInvalid_validBundleWithAdditionalInvalidCategory_null() {
+        // Create valid data label and put an additional invalid/unknown category key
+        val dataLabelBundle = createDataLabelPersistableBundleWithAdditonalInvalidCategory()
+
+        val dataCategoryMap =
+            DataCategory.getDataCategoryMap(dataLabelBundle, "invalid_datausage_key")
+
+        assertThat(dataCategoryMap).isNotNull()
+        assertThat(dataCategoryMap).isEmpty()
+    }
+
+    @Test
+    fun getDataCategory_nullBundle_nullDataCategory() {
+        val dataCategory =
+            DataCategory.getDataCategory(
+                null, DataLabelConstants.DATA_USAGE_SHARED, CATEGORY_LOCATION)
+
+        assertThat(dataCategory).isNull()
+    }
+
+    @Test
+    fun getDataCategory_emptyBundle_nullDataCategory() {
+        val dataCategory =
+            DataCategory.getDataCategory(
+                PersistableBundle.EMPTY, DataLabelConstants.DATA_USAGE_SHARED, CATEGORY_LOCATION)
+
+        assertThat(dataCategory).isNull()
+    }
+
+    @Test
+    fun getDataCategory_invalidBundle_nullDataCategory() {
+        val dataCategory =
+            DataCategory.getDataCategory(
+                createInvalidCategoryMapPersistableBundle(),
+                DataLabelConstants.DATA_USAGE_SHARED,
+                CATEGORY_LOCATION)
+
+        assertThat(dataCategory).isNull()
+    }
+
+    @Test
+    fun getDataCategory_validCategoriesAndInvalidType_nullDataCategory() {
+        val dataCategory =
+            DataCategory.getDataCategory(
+                createCategoryMapPersistableBundleWithInvalidTypes(),
+                DataLabelConstants.DATA_USAGE_SHARED,
+                INVALID_KEY)
+
+        assertThat(dataCategory).isNull()
+    }
+
+    @Test
+    fun getDataCategory_validBundle_validCategoryAndExpectedTypes() {
+        val dataCategory =
+            DataCategory.getDataCategory(
+                createCategoryMapPersistableBundle(),
+                DataLabelConstants.DATA_USAGE_SHARED,
+                CATEGORY_LOCATION)
+
+        assertThat(dataCategory).isNotNull()
+        assertThat(dataCategory?.dataTypes).isNotEmpty()
+        assertThat(dataCategory?.dataTypes?.keys)
+            .containsExactlyElementsIn(
+                DataTypeConstants.getValidDataTypesForCategory(CATEGORY_LOCATION))
+    }
+
+    @Test
+    fun getDataCategory_validBundleWithAddedInvalidType_validCategoryAndOnlyExpectedTypes() {
+        // Create valid bundle with additional invalid type
+        val dataTypeMapBundle = createCategoryMapPersistableBundle()
+        dataTypeMapBundle.putPersistableBundle(INVALID_KEY, createTypePersistableBundle())
+
+        val dataCategory =
+            DataCategory.getDataCategory(
+                dataTypeMapBundle, DataLabelConstants.DATA_USAGE_SHARED, CATEGORY_LOCATION)
+
+        assertThat(dataCategory).isNotNull()
+        assertThat(dataCategory?.dataTypes).isNotEmpty()
+        assertThat(dataCategory?.dataTypes?.keys)
+            .containsExactlyElementsIn(
+                DataTypeConstants.getValidDataTypesForCategory(CATEGORY_LOCATION))
+    }
+}
diff --git a/SafetyLabel/tests/java/com/android/permission/safetylabel/DataLabelTest.kt b/SafetyLabel/tests/java/com/android/permission/safetylabel/DataLabelTest.kt
new file mode 100644
index 0000000..1c0c562
--- /dev/null
+++ b/SafetyLabel/tests/java/com/android/permission/safetylabel/DataLabelTest.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permission.safetylabel
+
+import android.os.PersistableBundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createInvalidSafetyLabelPersistableBundle
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createSafetyLabelPersistableBundle
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createSafetyLabelPersistableBundleWithEmptyDataCollected
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createSafetyLabelPersistableBundleWithEmptyDataShared
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createSafetyLabelPersistableBundleWithInvalidDataCollected
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createSafetyLabelPersistableBundleWithInvalidDataShared
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createSafetyLabelPersistableBundleWithNullDataCollected
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createSafetyLabelPersistableBundleWithNullDataShared
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** CTS tests for [DataLabel]. */
+@RunWith(AndroidJUnit4::class)
+class DataLabelTest {
+    @Test
+    fun getDataLabel_nullBundle_nullDataLabel() {
+        val dataLabel: DataLabel? = DataLabel.getDataLabel(null)
+
+        assertThat(dataLabel).isNull()
+    }
+
+    @Test
+    fun getDataLabel_emptyBundle_nullDataLabel() {
+        val dataLabel: DataLabel? = DataLabel.getDataLabel(PersistableBundle.EMPTY)
+
+        assertThat(dataLabel).isNull()
+    }
+
+    @Test
+    fun getDataLabel_invalidBundle_nullDataLabel() {
+        val dataLabel: DataLabel? =
+            DataLabel.getDataLabel(createInvalidSafetyLabelPersistableBundle())
+
+        assertThat(dataLabel).isNull()
+    }
+
+    @Test
+    fun getDataLabel_nullDataCollectedBundle_dataCollectedEmpty() {
+        val dataLabel: DataLabel? =
+            DataLabel.getDataLabel(createSafetyLabelPersistableBundleWithNullDataCollected())
+
+        assertThat(dataLabel).isNotNull()
+        assertThat(dataLabel?.dataCollected).isEmpty()
+        assertThat(dataLabel?.dataShared).isNotEmpty()
+    }
+
+    @Test
+    fun getDataLabel_nullDataSharedBundle_dataSharedEmpty() {
+        val dataLabel: DataLabel? =
+            DataLabel.getDataLabel(createSafetyLabelPersistableBundleWithNullDataShared())
+
+        assertThat(dataLabel).isNotNull()
+        assertThat(dataLabel?.dataCollected).isNotEmpty()
+        assertThat(dataLabel?.dataShared).isEmpty()
+    }
+
+    @Test
+    fun getDataLabel_emptyDataCollectedBundle_dataCollectedEmpty() {
+        val dataLabel: DataLabel? =
+            DataLabel.getDataLabel(createSafetyLabelPersistableBundleWithEmptyDataCollected())
+
+        assertThat(dataLabel).isNotNull()
+        assertThat(dataLabel?.dataCollected).isEmpty()
+        assertThat(dataLabel?.dataShared).isNotEmpty()
+    }
+
+    @Test
+    fun getDataLabel_emptyDataSharedBundle_dataSharedEmpty() {
+        val dataLabel: DataLabel? =
+            DataLabel.getDataLabel(createSafetyLabelPersistableBundleWithEmptyDataShared())
+
+        assertThat(dataLabel).isNotNull()
+        assertThat(dataLabel?.dataCollected).isNotEmpty()
+        assertThat(dataLabel?.dataShared).isEmpty()
+    }
+
+    @Test
+    fun getDataLabel_invalidDataCollectedBundle_dataCollectedEmpty() {
+        val dataLabel: DataLabel? =
+            DataLabel.getDataLabel(createSafetyLabelPersistableBundleWithInvalidDataCollected())
+
+        assertThat(dataLabel).isNotNull()
+        assertThat(dataLabel?.dataCollected).isEmpty()
+        assertThat(dataLabel?.dataShared).isNotEmpty()
+    }
+
+    @Test
+    fun getDataLabel_invalidDataSharedBundle_dataSharedEmpty() {
+        val dataLabel: DataLabel? =
+            DataLabel.getDataLabel(createSafetyLabelPersistableBundleWithInvalidDataShared())
+
+        assertThat(dataLabel).isNotNull()
+        assertThat(dataLabel?.dataCollected).isNotEmpty()
+        assertThat(dataLabel?.dataShared).isEmpty()
+    }
+
+    @Test
+    fun getDataLabel_validBundle() {
+        val dataLabel: DataLabel? = DataLabel.getDataLabel(createSafetyLabelPersistableBundle())
+
+        assertThat(dataLabel).isNotNull()
+        assertThat(dataLabel?.dataCollected).isNotEmpty()
+        assertThat(dataLabel?.dataShared).isNotEmpty()
+    }
+}
diff --git a/SafetyLabel/tests/java/com/android/permission/safetylabel/DataTypeTest.kt b/SafetyLabel/tests/java/com/android/permission/safetylabel/DataTypeTest.kt
new file mode 100644
index 0000000..bfc9be7
--- /dev/null
+++ b/SafetyLabel/tests/java/com/android/permission/safetylabel/DataTypeTest.kt
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permission.safetylabel
+
+import android.os.PersistableBundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.permission.safetylabel.DataCategoryConstants.CATEGORY_LOCATION
+import com.android.permission.safetylabel.DataPurposeConstants.PURPOSE_ADVERTISING
+import com.android.permission.safetylabel.DataPurposeConstants.PURPOSE_APP_FUNCTIONALITY
+import com.android.permission.safetylabel.DataType.KEY_EPHEMERAL
+import com.android.permission.safetylabel.DataType.KEY_PURPOSES
+import com.android.permission.safetylabel.DataType.KEY_USER_CONTROL
+import com.android.permission.safetylabel.DataTypeConstants.LOCATION_APPROX_LOCATION
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.INVALID_KEY
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createInvalidTypeMapPersistableBundle
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createInvalidTypePersistableBundle
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createTypeMapPersistableBundle
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createTypeMapWithInvalidTypeDataPersistableBundle
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createTypePersistableBundle
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** CTS tests for [DataType]. */
+@RunWith(AndroidJUnit4::class)
+class DataTypeTest {
+    @Test
+    fun getDataTypeMap_invalidCategory_nullPersistableBundle_emptyMap() {
+        val dataTypeMap =
+            DataType.getDataTypeMap(null, DataLabelConstants.DATA_USAGE_SHARED, INVALID_KEY)
+
+        assertThat(dataTypeMap).isNotNull()
+        assertThat(dataTypeMap).isEmpty()
+    }
+
+    @Test
+    fun getDataTypeMap_invalidCategory_emptyPersistableBundle_emptyMap() {
+        val dataTypeMap =
+            DataType.getDataTypeMap(
+                PersistableBundle.EMPTY, DataLabelConstants.DATA_USAGE_SHARED, INVALID_KEY)
+
+        assertThat(dataTypeMap).isNotNull()
+        assertThat(dataTypeMap).isEmpty()
+    }
+
+    @Test
+    fun getDataTypeMap_invalidCategory_invalidBundle_emptyMap() {
+        val dataTypeMap =
+            DataType.getDataTypeMap(
+                createInvalidTypeMapPersistableBundle(),
+                DataLabelConstants.DATA_USAGE_SHARED,
+                INVALID_KEY)
+
+        assertThat(dataTypeMap).isNotNull()
+        assertThat(dataTypeMap).isEmpty()
+    }
+
+    @Test
+    fun getDataTypeMap_invalidCategory_validBundle_emptyMap() {
+        val dataTypeMap =
+            DataType.getDataTypeMap(
+                createTypeMapPersistableBundle(CATEGORY_LOCATION),
+                DataLabelConstants.DATA_USAGE_SHARED,
+                INVALID_KEY)
+
+        assertThat(dataTypeMap).isNotNull()
+        assertThat(dataTypeMap).isEmpty()
+    }
+
+    @Test
+    fun getDataTypeMap_validCategory_nullPersistableBundle_emptyMap() {
+        val dataTypeMap =
+            DataType.getDataTypeMap(null, DataLabelConstants.DATA_USAGE_SHARED, CATEGORY_LOCATION)
+
+        assertThat(dataTypeMap).isNotNull()
+        assertThat(dataTypeMap).isEmpty()
+    }
+
+    @Test
+    fun getDataTypeMap_validCategory_emptyPersistableBundle_emptyMap() {
+        val dataTypeMap =
+            DataType.getDataTypeMap(
+                PersistableBundle.EMPTY, DataLabelConstants.DATA_USAGE_SHARED, CATEGORY_LOCATION)
+
+        assertThat(dataTypeMap).isNotNull()
+        assertThat(dataTypeMap).isEmpty()
+    }
+
+    @Test
+    fun getDataTypeMap_validCategory_invalidBundle_emptyMap() {
+        val dataTypeMap =
+            DataType.getDataTypeMap(
+                createInvalidTypeMapPersistableBundle(),
+                DataLabelConstants.DATA_USAGE_SHARED,
+                CATEGORY_LOCATION)
+
+        assertThat(dataTypeMap).isNotNull()
+        assertThat(dataTypeMap).isEmpty()
+    }
+
+    @Test
+    fun getDataTypeMap_validCategory_validBundle_hasAllExpectedTypes() {
+        val typeMapPersistableBundle = createTypeMapPersistableBundle(CATEGORY_LOCATION)
+
+        val dataTypeMap =
+            DataType.getDataTypeMap(
+                typeMapPersistableBundle, DataLabelConstants.DATA_USAGE_SHARED, CATEGORY_LOCATION)
+
+        assertThat(dataTypeMap).isNotNull()
+        assertThat(dataTypeMap.keys)
+            .containsExactlyElementsIn(
+                DataTypeConstants.getValidDataTypesForCategory(CATEGORY_LOCATION))
+    }
+
+    @Test
+    fun getDataTypeMap_validCategory_validBundleWithAddedInvalidType_hasOnlyExpectedTypes() {
+        val typeMapPersistableBundle = createTypeMapPersistableBundle(CATEGORY_LOCATION)
+        // Add additional valid persistable bundle under invalid key
+        typeMapPersistableBundle.putPersistableBundle(INVALID_KEY, createTypePersistableBundle())
+
+        val dataTypeMap =
+            DataType.getDataTypeMap(
+                typeMapPersistableBundle, DataLabelConstants.DATA_USAGE_SHARED, CATEGORY_LOCATION)
+
+        assertThat(dataTypeMap).isNotNull()
+        assertThat(dataTypeMap.keys)
+            .containsExactlyElementsIn(
+                DataTypeConstants.getValidDataTypesForCategory(CATEGORY_LOCATION))
+        assertThat(dataTypeMap.keys).doesNotContain(INVALID_KEY)
+    }
+
+    @Test
+    fun getDataTypeMap_validCategory_validType_invalidData_emptyMap() {
+        val typeMapPersistableBundle =
+            createTypeMapWithInvalidTypeDataPersistableBundle(CATEGORY_LOCATION)
+
+        val dataTypeMap =
+            DataType.getDataTypeMap(
+                typeMapPersistableBundle, DataLabelConstants.DATA_USAGE_SHARED, CATEGORY_LOCATION)
+
+        assertThat(dataTypeMap).isNotNull()
+        assertThat(dataTypeMap).isEmpty()
+    }
+
+    @Test
+    fun getDataTypeMap_dataCollected_validCategory_validBundle_validateSingleExpectedType() {
+        val dataTypeMap =
+            DataType.getDataTypeMap(
+                createTypeMapPersistableBundle(CATEGORY_LOCATION),
+                DataLabelConstants.DATA_USAGE_COLLECTED,
+                CATEGORY_LOCATION)
+
+        assertThat(dataTypeMap).isNotNull()
+        assertThat(dataTypeMap).containsKey(LOCATION_APPROX_LOCATION)
+        val type = dataTypeMap[LOCATION_APPROX_LOCATION]!!
+        assertThat(type.purposeSet).containsExactly(PURPOSE_APP_FUNCTIONALITY, PURPOSE_ADVERTISING)
+        assertThat(type.userControl).isTrue()
+        assertThat(type.ephemeral).isTrue()
+    }
+
+    @Test
+    fun getDataTypeMap_dataShared_validCategory_validBundle_validateSingleExpectedType() {
+        val dataTypeMap =
+            DataType.getDataTypeMap(
+                createTypeMapPersistableBundle(CATEGORY_LOCATION),
+                DataLabelConstants.DATA_USAGE_SHARED,
+                CATEGORY_LOCATION)
+
+        assertThat(dataTypeMap).isNotNull()
+        assertThat(dataTypeMap).containsKey(LOCATION_APPROX_LOCATION)
+        val type = dataTypeMap[LOCATION_APPROX_LOCATION]!!
+        assertThat(type.purposeSet).containsExactly(PURPOSE_APP_FUNCTIONALITY, PURPOSE_ADVERTISING)
+        assertThat(type.userControl).isNull()
+        assertThat(type.ephemeral).isNull()
+    }
+
+    @Test
+    fun getDataType_invalidBundle_nullDataType() {
+        val dataType =
+            DataType.getDataType(
+                createInvalidTypePersistableBundle(), DataLabelConstants.DATA_USAGE_SHARED)
+
+        assertThat(dataType).isNull()
+    }
+
+    @Test
+    fun getDataType_dataCollected_validDataType() {
+        val dataType =
+            DataType.getDataType(
+                createTypePersistableBundle(), DataLabelConstants.DATA_USAGE_COLLECTED)
+
+        assertThat(dataType).isNotNull()
+        assertThat(dataType?.purposeSet)
+            .containsExactly(PURPOSE_APP_FUNCTIONALITY, PURPOSE_ADVERTISING)
+        assertThat(dataType?.userControl).isTrue()
+        assertThat(dataType?.ephemeral).isTrue()
+    }
+
+    @Test
+    fun getDataType_dataShared_validDataType() {
+        val dataType =
+            DataType.getDataType(
+                createTypePersistableBundle(), DataLabelConstants.DATA_USAGE_SHARED)
+
+        assertThat(dataType).isNotNull()
+        assertThat(dataType?.purposeSet)
+            .containsExactly(PURPOSE_APP_FUNCTIONALITY, PURPOSE_ADVERTISING)
+        assertThat(dataType?.userControl).isNull()
+        assertThat(dataType?.ephemeral).isNull()
+    }
+
+    @Test
+    fun getDataType_validDataTypeWithAddedInvalidPurpose_onlyValidPurposes() {
+        val typePersistableBundle = createTypePersistableBundle()
+        val purposes: IntArray = typePersistableBundle.getIntArray(KEY_PURPOSES)!!
+        val updatedPurposes: IntArray = intArrayOf(-1, *purposes)
+        typePersistableBundle.putIntArray(KEY_PURPOSES, updatedPurposes)
+
+        val dataType =
+            DataType.getDataType(typePersistableBundle, DataLabelConstants.DATA_USAGE_SHARED)
+
+        assertThat(dataType).isNotNull()
+        // Should not contain the additional "-1" purpose added above
+        assertThat(dataType?.purposeSet)
+            .containsExactly(PURPOSE_APP_FUNCTIONALITY, PURPOSE_ADVERTISING)
+    }
+
+    @Test
+    fun getDataType_dataTypeWithInvalidPurpose_nullDataType() {
+        val typePersistableBundle = createTypePersistableBundle()
+        typePersistableBundle.remove(KEY_PURPOSES)
+        val updatedPurposes: IntArray = intArrayOf(-1)
+        typePersistableBundle.putIntArray(KEY_PURPOSES, updatedPurposes)
+
+        val dataType =
+            DataType.getDataType(typePersistableBundle, DataLabelConstants.DATA_USAGE_SHARED)
+
+        assertThat(dataType).isNull()
+    }
+
+    @Test
+    fun getDataType_noPurpose_nullDataType() {
+        val typePersistableBundle = createTypePersistableBundle()
+        typePersistableBundle.remove(KEY_PURPOSES)
+
+        val dataType =
+            DataType.getDataType(typePersistableBundle, DataLabelConstants.DATA_USAGE_SHARED)
+
+        assertThat(dataType).isNull()
+    }
+
+    @Test
+    fun getDataType_dataCollected_validDataType_noUserControl_noEphemeral() {
+        val bundle = createTypePersistableBundle()
+        bundle.remove(KEY_USER_CONTROL)
+        bundle.remove(KEY_EPHEMERAL)
+
+        val dataType = DataType.getDataType(bundle, DataLabelConstants.DATA_USAGE_COLLECTED)
+
+        assertThat(dataType).isNotNull()
+        assertThat(dataType?.purposeSet)
+            .containsExactly(PURPOSE_APP_FUNCTIONALITY, PURPOSE_ADVERTISING)
+        assertThat(dataType?.userControl).isNull()
+        assertThat(dataType?.ephemeral).isNull()
+    }
+}
diff --git a/SafetyLabel/tests/java/com/android/permission/safetylabel/SafetyLabelTest.kt b/SafetyLabel/tests/java/com/android/permission/safetylabel/SafetyLabelTest.kt
new file mode 100644
index 0000000..ce9f246
--- /dev/null
+++ b/SafetyLabel/tests/java/com/android/permission/safetylabel/SafetyLabelTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permission.safetylabel
+
+import android.os.PersistableBundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createInvalidMetadataPersistableBundle
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createMetadataPersistableBundle
+import com.android.permission.safetylabel.SafetyLabelTestPersistableBundles.createMetadataPersistableBundleWithInvalidSafetyLabel
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** CTS tests for [SafetyLabel]. */
+@RunWith(AndroidJUnit4::class)
+class SafetyLabelTest {
+
+    @Test
+    fun getSafetyLabelFromMetaData_nullMetadataBundle_nullSafetyLabel() {
+        val safetyLabel = SafetyLabel.getSafetyLabelFromMetadata(null)
+
+        assertThat(safetyLabel).isNull()
+    }
+
+    @Test
+    fun getSafetyLabelFromMetaData_emptyMetadataBundle_nullSafetyLabel() {
+        val safetyLabel = SafetyLabel.getSafetyLabelFromMetadata(PersistableBundle.EMPTY)
+
+        assertThat(safetyLabel).isNull()
+    }
+
+    @Test
+    fun getSafetyLabelFromMetaData_invalidMetadataBundle_nullSafetyLabel() {
+        val safetyLabel =
+            SafetyLabel.getSafetyLabelFromMetadata(createInvalidMetadataPersistableBundle())
+
+        assertThat(safetyLabel).isNull()
+    }
+
+    @Test
+    fun getSafetyLabelFromMetaData_invalidSafetyLabelBundle_dataSharedEmpty() {
+        val safetyLabel =
+            SafetyLabel.getSafetyLabelFromMetadata(
+                createMetadataPersistableBundleWithInvalidSafetyLabel())
+
+        assertThat(safetyLabel).isNull()
+    }
+
+    @Test
+    fun getSafetyLabelFromMetaData_validBundle_hasDataShared() {
+        val safetyLabel = SafetyLabel.getSafetyLabelFromMetadata(createMetadataPersistableBundle())
+
+        assertThat(safetyLabel).isNotNull()
+        assertThat(safetyLabel?.dataLabel).isNotNull()
+    }
+}
diff --git a/SafetyLabel/tests/java/com/android/permission/safetylabel/SafetyLabelTestPersistableBundles.kt b/SafetyLabel/tests/java/com/android/permission/safetylabel/SafetyLabelTestPersistableBundles.kt
new file mode 100644
index 0000000..2d28116
--- /dev/null
+++ b/SafetyLabel/tests/java/com/android/permission/safetylabel/SafetyLabelTestPersistableBundles.kt
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.permission.safetylabel
+
+import android.os.PersistableBundle
+import com.android.permission.safetylabel.DataCategoryConstants.CATEGORY_LOCATION
+import com.android.permission.safetylabel.DataCategoryConstants.Category
+import com.android.permission.safetylabel.DataPurposeConstants.PURPOSE_ADVERTISING
+import com.android.permission.safetylabel.DataPurposeConstants.PURPOSE_APP_FUNCTIONALITY
+import com.android.permission.safetylabel.DataType.KEY_EPHEMERAL
+import com.android.permission.safetylabel.DataType.KEY_PURPOSES
+import com.android.permission.safetylabel.DataType.KEY_USER_CONTROL
+
+/** A class that facilitates creating test safety label persistable bundles. */
+object SafetyLabelTestPersistableBundles {
+    const val INVALID_KEY = "invalid_key"
+
+    fun createMetadataPersistableBundle(): PersistableBundle {
+        return PersistableBundle().apply {
+            putPersistableBundle(SafetyLabel.KEY_SAFETY_LABEL, createSafetyLabelPersistableBundle())
+        }
+    }
+
+    fun createInvalidMetadataPersistableBundle(): PersistableBundle {
+        return PersistableBundle().apply {
+            putPersistableBundle(INVALID_KEY, createSafetyLabelPersistableBundle())
+        }
+    }
+
+    fun createMetadataPersistableBundleWithInvalidSafetyLabel(): PersistableBundle {
+        return PersistableBundle().apply {
+            putPersistableBundle(
+                SafetyLabel.KEY_SAFETY_LABEL, createInvalidSafetyLabelPersistableBundle())
+        }
+    }
+
+    /** Returns [PersistableBundle] representation of a valid safety label */
+    fun createSafetyLabelPersistableBundle(): PersistableBundle {
+        return PersistableBundle().apply {
+            putPersistableBundle(DataLabel.KEY_DATA_LABEL, createDataLabelPersistableBundle())
+        }
+    }
+
+    /** Returns [PersistableBundle] representation of an ivnalid safety label */
+    fun createInvalidSafetyLabelPersistableBundle(): PersistableBundle {
+        return PersistableBundle().apply {
+            putPersistableBundle(INVALID_KEY, createDataLabelPersistableBundle())
+        }
+    }
+
+    /** Returns [PersistableBundle] representation of an ivnalid safety label */
+    fun createSafetyLabelPersistableBundleWithNullDataCollected(): PersistableBundle {
+        return PersistableBundle().apply {
+            putPersistableBundle(
+                DataLabel.KEY_DATA_LABEL, createDataLabelPersistableBundleWithNullDataCollected())
+        }
+    }
+
+    /** Returns [PersistableBundle] representation of an ivnalid safety label */
+    fun createSafetyLabelPersistableBundleWithNullDataShared(): PersistableBundle {
+        return PersistableBundle().apply {
+            putPersistableBundle(
+                DataLabel.KEY_DATA_LABEL, createDataLabelPersistableBundleWithNullDataShared())
+        }
+    }
+
+    /** Returns [PersistableBundle] representation of an ivnalid safety label */
+    fun createSafetyLabelPersistableBundleWithEmptyDataCollected(): PersistableBundle {
+        return PersistableBundle().apply {
+            putPersistableBundle(
+                DataLabel.KEY_DATA_LABEL, createDataLabelPersistableBundleWithEmptyDataCollected())
+        }
+    }
+
+    /** Returns [PersistableBundle] representation of an ivnalid safety label */
+    fun createSafetyLabelPersistableBundleWithEmptyDataShared(): PersistableBundle {
+        return PersistableBundle().apply {
+            putPersistableBundle(
+                DataLabel.KEY_DATA_LABEL, createDataLabelPersistableBundleWithEmptyDataShared())
+        }
+    }
+
+    /** Returns [PersistableBundle] representation of an ivnalid safety label */
+    fun createSafetyLabelPersistableBundleWithInvalidDataCollected(): PersistableBundle {
+        return PersistableBundle().apply {
+            putPersistableBundle(
+                DataLabel.KEY_DATA_LABEL,
+                createDataLabelPersistableBundleWithInvalidDataCollected())
+        }
+    }
+
+    /** Returns [PersistableBundle] representation of an ivnalid safety label */
+    fun createSafetyLabelPersistableBundleWithInvalidDataShared(): PersistableBundle {
+        return PersistableBundle().apply {
+            putPersistableBundle(
+                DataLabel.KEY_DATA_LABEL, createDataLabelPersistableBundleWithInvalidDataShared())
+        }
+    }
+
+    fun createDataLabelPersistableBundle(): PersistableBundle {
+        return PersistableBundle().apply {
+            putPersistableBundle(
+                DataLabelConstants.DATA_USAGE_SHARED, createCategoryMapPersistableBundle())
+            putPersistableBundle(
+                DataLabelConstants.DATA_USAGE_COLLECTED, createCategoryMapPersistableBundle())
+        }
+    }
+
+    fun createInvalidDataLabelPersistableBundle(): PersistableBundle {
+        return PersistableBundle().apply {
+            putPersistableBundle(INVALID_KEY, createCategoryMapPersistableBundle())
+        }
+    }
+
+    private fun createDataLabelPersistableBundleWithNullDataCollected(): PersistableBundle {
+        val bundle = createDataLabelPersistableBundle()
+        bundle.remove(DataLabelConstants.DATA_USAGE_COLLECTED)
+        return bundle
+    }
+
+    private fun createDataLabelPersistableBundleWithNullDataShared(): PersistableBundle {
+        val bundle = createDataLabelPersistableBundle()
+        bundle.remove(DataLabelConstants.DATA_USAGE_SHARED)
+        return bundle
+    }
+
+    private fun createDataLabelPersistableBundleWithEmptyDataCollected(): PersistableBundle {
+        val bundle = createDataLabelPersistableBundle()
+        bundle.remove(DataLabelConstants.DATA_USAGE_COLLECTED)
+        bundle.putPersistableBundle(
+            DataLabelConstants.DATA_USAGE_COLLECTED, PersistableBundle.EMPTY)
+        return bundle
+    }
+
+    private fun createDataLabelPersistableBundleWithEmptyDataShared(): PersistableBundle {
+        val bundle = createDataLabelPersistableBundle()
+        bundle.remove(DataLabelConstants.DATA_USAGE_SHARED)
+        bundle.putPersistableBundle(DataLabelConstants.DATA_USAGE_SHARED, PersistableBundle.EMPTY)
+        return bundle
+    }
+
+    private fun createDataLabelPersistableBundleWithInvalidDataCollected(): PersistableBundle {
+        val bundle = createDataLabelPersistableBundle()
+        bundle.remove(DataLabelConstants.DATA_USAGE_COLLECTED)
+        bundle.putPersistableBundle(
+            DataLabelConstants.DATA_USAGE_COLLECTED, createInvalidCategoryMapPersistableBundle())
+        return bundle
+    }
+
+    private fun createDataLabelPersistableBundleWithInvalidDataShared(): PersistableBundle {
+        val bundle = createDataLabelPersistableBundle()
+        bundle.remove(DataLabelConstants.DATA_USAGE_SHARED)
+        bundle.putPersistableBundle(
+            DataLabelConstants.DATA_USAGE_SHARED, createInvalidCategoryMapPersistableBundle())
+        return bundle
+    }
+
+    fun createDataLabelPersistableBundleWithAdditonalInvalidCategory(): PersistableBundle {
+        return PersistableBundle().apply {
+            putPersistableBundle(
+                DataLabelConstants.DATA_USAGE_SHARED,
+                createCategoryMapPersistableBundleWithAdditionalInvalidCategory())
+            putPersistableBundle(
+                DataLabelConstants.DATA_USAGE_COLLECTED,
+                createCategoryMapPersistableBundleWithAdditionalInvalidCategory())
+        }
+    }
+
+    /** Returns [PersistableBundle] representation of a [Map] of valid data categories */
+    fun createCategoryMapPersistableBundle(): PersistableBundle {
+        return PersistableBundle().apply {
+            DataCategoryConstants.VALID_CATEGORIES.forEach { categoryKey ->
+                putPersistableBundle(categoryKey, createTypeMapPersistableBundle(categoryKey))
+            }
+        }
+    }
+
+    /** Returns [PersistableBundle] representation of a [Map] of valid data categories */
+    fun createCategoryMapPersistableBundleWithAdditionalInvalidCategory(): PersistableBundle {
+        return PersistableBundle().apply {
+            DataCategoryConstants.VALID_CATEGORIES.forEach { categoryKey ->
+                putPersistableBundle(categoryKey, createTypeMapPersistableBundle(categoryKey))
+            }
+            putPersistableBundle(INVALID_KEY, createTypeMapPersistableBundle(CATEGORY_LOCATION))
+        }
+    }
+
+    /**
+     * Returns [PersistableBundle] representation of a [Map] of valid data categories and invalid
+     * types
+     */
+    fun createCategoryMapPersistableBundleWithInvalidTypes(): PersistableBundle {
+        return PersistableBundle().apply {
+            DataCategoryConstants.VALID_CATEGORIES.forEach { categoryKey ->
+                putPersistableBundle(categoryKey, createInvalidTypeMapPersistableBundle())
+            }
+        }
+    }
+
+    /** Returns [PersistableBundle] representation of a [Map] of invalid data categories */
+    fun createInvalidCategoryMapPersistableBundle(): PersistableBundle {
+        return PersistableBundle().apply {
+            putPersistableBundle(INVALID_KEY, createTypeMapPersistableBundle(CATEGORY_LOCATION))
+        }
+    }
+
+    /** Returns [PersistableBundle] representation of a [Map] of valid data type */
+    fun createTypeMapPersistableBundle(@Category category: String): PersistableBundle {
+        return PersistableBundle().apply {
+            DataTypeConstants.getValidDataTypesForCategory(category).forEach { type ->
+                putPersistableBundle(type, createTypePersistableBundle())
+            }
+        }
+    }
+
+    /** Returns [PersistableBundle] representation of a [Map] of invalid data type */
+    fun createInvalidTypeMapPersistableBundle(): PersistableBundle {
+        return PersistableBundle().apply {
+            putPersistableBundle(INVALID_KEY, createTypePersistableBundle())
+        }
+    }
+
+    /** Returns [PersistableBundle] representation of a [Map] of valid type, with invalid data */
+    fun createTypeMapWithInvalidTypeDataPersistableBundle(
+        @Category category: String
+    ): PersistableBundle {
+        return PersistableBundle().apply {
+            DataTypeConstants.getValidDataTypesForCategory(category).forEach { type ->
+                putPersistableBundle(type, createInvalidTypePersistableBundle())
+            }
+        }
+    }
+
+    /** Returns [PersistableBundle] representation of a valid data type */
+    fun createTypePersistableBundle(): PersistableBundle {
+        return PersistableBundle().apply {
+            putIntArray(KEY_PURPOSES, intArrayOf(PURPOSE_APP_FUNCTIONALITY, PURPOSE_ADVERTISING))
+            putBoolean(KEY_USER_CONTROL, true)
+            putBoolean(KEY_EPHEMERAL, true)
+        }
+    }
+
+    /** Returns [PersistableBundle] representation of an invalid data type */
+    fun createInvalidTypePersistableBundle(): PersistableBundle {
+        return PersistableBundle().apply { putLong(INVALID_KEY, 0) }
+    }
+}
diff --git a/framework-s/Android.bp b/framework-s/Android.bp
index d5cef24..12c8656 100644
--- a/framework-s/Android.bp
+++ b/framework-s/Android.bp
@@ -34,8 +34,8 @@
 }
 
 filegroup {
-    name: "safetycenter-config-schema",
-    srcs: ["java/android/safetycenter/config/safety_center_config.xsd"],
+    name: "safetycenter-config-schemas",
+    srcs: ["java/android/safetycenter/config/safety_center_config*.xsd"],
     path: "java/android/safetycenter/config/",
     visibility: ["//packages/modules/Permission/SafetyCenter/ConfigLintChecker"],
 }
@@ -47,6 +47,9 @@
         "framework-annotations-lib",
         "unsupportedappusage",
     ],
+    static_libs: [
+        "modules-utils-build",
+    ],
     apex_available: [
         "com.android.permission",
         "test_com.android.permission",
@@ -89,6 +92,7 @@
         "android.permission",
         "android.app.role",
         "android.safetycenter",
+        "android.safetylabel",
         // For com.android.permission.jarjar.
         "com.android.permission",
     ],
diff --git a/framework-s/api/system-current.txt b/framework-s/api/system-current.txt
index db2d140..377f3ce 100644
--- a/framework-s/api/system-current.txt
+++ b/framework-s/api/system-current.txt
@@ -36,6 +36,7 @@
     method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>);
     field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1
     field public static final String ROLE_DEVICE_POLICY_MANAGEMENT = "android.app.role.DEVICE_POLICY_MANAGEMENT";
+    field public static final String ROLE_FINANCED_DEVICE_KIOSK = "android.app.role.FINANCED_DEVICE_KIOSK";
     field public static final String ROLE_SYSTEM_ACTIVITY_RECOGNIZER = "android.app.role.SYSTEM_ACTIVITY_RECOGNIZER";
     field public static final String ROLE_SYSTEM_SUPERVISION = "android.app.role.SYSTEM_SUPERVISION";
     field public static final String ROLE_SYSTEM_WELLBEING = "android.app.role.SYSTEM_WELLBEING";
@@ -48,7 +49,9 @@
   public final class SafetyCenterData implements android.os.Parcelable {
     ctor public SafetyCenterData(@NonNull android.safetycenter.SafetyCenterStatus, @NonNull java.util.List<android.safetycenter.SafetyCenterIssue>, @NonNull java.util.List<android.safetycenter.SafetyCenterEntryOrGroup>, @NonNull java.util.List<android.safetycenter.SafetyCenterStaticEntryGroup>);
     method public int describeContents();
+    method @NonNull public java.util.List<android.safetycenter.SafetyCenterIssue> getDismissedIssues();
     method @NonNull public java.util.List<android.safetycenter.SafetyCenterEntryOrGroup> getEntriesOrGroups();
+    method @NonNull public android.os.Bundle getExtras();
     method @NonNull public java.util.List<android.safetycenter.SafetyCenterIssue> getIssues();
     method @NonNull public java.util.List<android.safetycenter.SafetyCenterStaticEntryGroup> getStaticEntryGroups();
     method @NonNull public android.safetycenter.SafetyCenterStatus getStatus();
@@ -56,6 +59,21 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.safetycenter.SafetyCenterData> CREATOR;
   }
 
+  public static final class SafetyCenterData.Builder {
+    ctor public SafetyCenterData.Builder(@NonNull android.safetycenter.SafetyCenterStatus);
+    ctor public SafetyCenterData.Builder(@NonNull android.safetycenter.SafetyCenterData);
+    method @NonNull public android.safetycenter.SafetyCenterData.Builder addDismissedIssue(@NonNull android.safetycenter.SafetyCenterIssue);
+    method @NonNull public android.safetycenter.SafetyCenterData.Builder addEntryOrGroup(@NonNull android.safetycenter.SafetyCenterEntryOrGroup);
+    method @NonNull public android.safetycenter.SafetyCenterData.Builder addIssue(@NonNull android.safetycenter.SafetyCenterIssue);
+    method @NonNull public android.safetycenter.SafetyCenterData.Builder addStaticEntryGroup(@NonNull android.safetycenter.SafetyCenterStaticEntryGroup);
+    method @NonNull public android.safetycenter.SafetyCenterData build();
+    method @NonNull public android.safetycenter.SafetyCenterData.Builder clearDismissedIssues();
+    method @NonNull public android.safetycenter.SafetyCenterData.Builder clearEntriesOrGroups();
+    method @NonNull public android.safetycenter.SafetyCenterData.Builder clearIssues();
+    method @NonNull public android.safetycenter.SafetyCenterData.Builder clearStaticEntryGroups();
+    method @NonNull public android.safetycenter.SafetyCenterData.Builder setExtras(@NonNull android.os.Bundle);
+  }
+
   public final class SafetyCenterEntry implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.safetycenter.SafetyCenterEntry.IconAction getIconAction();
@@ -149,6 +167,8 @@
   public final class SafetyCenterIssue implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public java.util.List<android.safetycenter.SafetyCenterIssue.Action> getActions();
+    method @Nullable public CharSequence getAttributionTitle();
+    method @Nullable public String getGroupId();
     method @NonNull public String getId();
     method public int getSeverityLevel();
     method @Nullable public CharSequence getSubtitle();
@@ -177,6 +197,7 @@
 
   public static final class SafetyCenterIssue.Action.Builder {
     ctor public SafetyCenterIssue.Action.Builder(@NonNull String, @NonNull CharSequence, @NonNull android.app.PendingIntent);
+    ctor public SafetyCenterIssue.Action.Builder(@NonNull android.safetycenter.SafetyCenterIssue.Action);
     method @NonNull public android.safetycenter.SafetyCenterIssue.Action build();
     method @NonNull public android.safetycenter.SafetyCenterIssue.Action.Builder setId(@NonNull String);
     method @NonNull public android.safetycenter.SafetyCenterIssue.Action.Builder setIsInFlight(boolean);
@@ -191,7 +212,9 @@
     ctor public SafetyCenterIssue.Builder(@NonNull android.safetycenter.SafetyCenterIssue);
     method @NonNull public android.safetycenter.SafetyCenterIssue build();
     method @NonNull public android.safetycenter.SafetyCenterIssue.Builder setActions(@NonNull java.util.List<android.safetycenter.SafetyCenterIssue.Action>);
+    method @NonNull public android.safetycenter.SafetyCenterIssue.Builder setAttributionTitle(@Nullable CharSequence);
     method @NonNull public android.safetycenter.SafetyCenterIssue.Builder setDismissible(boolean);
+    method @NonNull public android.safetycenter.SafetyCenterIssue.Builder setGroupId(@Nullable String);
     method @NonNull public android.safetycenter.SafetyCenterIssue.Builder setId(@NonNull String);
     method @NonNull public android.safetycenter.SafetyCenterIssue.Builder setSeverityLevel(int);
     method @NonNull public android.safetycenter.SafetyCenterIssue.Builder setShouldConfirmDismissal(boolean);
@@ -211,6 +234,7 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE) public android.safetycenter.SafetySourceData getSafetySourceData(@NonNull String);
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_SAFETY_CENTER_STATUS, android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE}) public boolean isSafetyCenterEnabled();
     method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void refreshSafetySources(int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void refreshSafetySources(int, @NonNull java.util.List<java.lang.String>);
     method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void removeOnSafetyCenterDataChangedListener(@NonNull android.safetycenter.SafetyCenterManager.OnSafetyCenterDataChangedListener);
     method @RequiresPermission(android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE) public void reportSafetySourceError(@NonNull String, @NonNull android.safetycenter.SafetySourceErrorDetails);
     method @RequiresPermission(android.Manifest.permission.MANAGE_SAFETY_CENTER) public void setSafetyCenterConfigForTests(@NonNull android.safetycenter.config.SafetyCenterConfig);
@@ -222,6 +246,7 @@
     field public static final String EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID = "android.safetycenter.extra.REFRESH_SAFETY_SOURCES_BROADCAST_ID";
     field public static final String EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE = "android.safetycenter.extra.REFRESH_SAFETY_SOURCES_REQUEST_TYPE";
     field public static final String EXTRA_REFRESH_SAFETY_SOURCE_IDS = "android.safetycenter.extra.REFRESH_SAFETY_SOURCE_IDS";
+    field public static final String EXTRA_SAFETY_SOURCES_GROUP_ID = "android.safetycenter.extra.SAFETY_SOURCES_GROUP_ID";
     field public static final String EXTRA_SAFETY_SOURCE_ID = "android.safetycenter.extra.SAFETY_SOURCE_ID";
     field public static final String EXTRA_SAFETY_SOURCE_ISSUE_ID = "android.safetycenter.extra.SAFETY_SOURCE_ISSUE_ID";
     field public static final String EXTRA_SAFETY_SOURCE_USER_HANDLE = "android.safetycenter.extra.SAFETY_SOURCE_USER_HANDLE";
@@ -229,6 +254,7 @@
     field public static final int REFRESH_REASON_DEVICE_REBOOT = 300; // 0x12c
     field public static final int REFRESH_REASON_OTHER = 600; // 0x258
     field public static final int REFRESH_REASON_PAGE_OPEN = 100; // 0x64
+    field public static final int REFRESH_REASON_PERIODIC = 700; // 0x2bc
     field public static final int REFRESH_REASON_RESCAN_BUTTON_CLICK = 200; // 0xc8
     field public static final int REFRESH_REASON_SAFETY_CENTER_ENABLED = 500; // 0x1f4
   }
@@ -310,6 +336,7 @@
 
   public static final class SafetyEvent.Builder {
     ctor public SafetyEvent.Builder(int);
+    ctor public SafetyEvent.Builder(@NonNull android.safetycenter.SafetyEvent);
     method @NonNull public android.safetycenter.SafetyEvent build();
     method @NonNull public android.safetycenter.SafetyEvent.Builder setRefreshBroadcastId(@Nullable String);
     method @NonNull public android.safetycenter.SafetyEvent.Builder setSafetySourceIssueActionId(@Nullable String);
@@ -318,6 +345,7 @@
 
   public final class SafetySourceData implements android.os.Parcelable {
     method public int describeContents();
+    method @NonNull public android.os.Bundle getExtras();
     method @NonNull public java.util.List<android.safetycenter.SafetySourceIssue> getIssues();
     method @Nullable public android.safetycenter.SafetySourceStatus getStatus();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -330,9 +358,11 @@
 
   public static final class SafetySourceData.Builder {
     ctor public SafetySourceData.Builder();
+    ctor public SafetySourceData.Builder(@NonNull android.safetycenter.SafetySourceData);
     method @NonNull public android.safetycenter.SafetySourceData.Builder addIssue(@NonNull android.safetycenter.SafetySourceIssue);
     method @NonNull public android.safetycenter.SafetySourceData build();
     method @NonNull public android.safetycenter.SafetySourceData.Builder clearIssues();
+    method @NonNull public android.safetycenter.SafetySourceData.Builder setExtras(@NonNull android.os.Bundle);
     method @NonNull public android.safetycenter.SafetySourceData.Builder setStatus(@Nullable android.safetycenter.SafetySourceStatus);
   }
 
@@ -347,9 +377,14 @@
   public final class SafetySourceIssue implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public java.util.List<android.safetycenter.SafetySourceIssue.Action> getActions();
+    method @Nullable public CharSequence getAttributionTitle();
+    method @Nullable public android.safetycenter.SafetySourceIssue.Notification getCustomNotification();
+    method @Nullable public String getDeduplicationId();
     method @NonNull public String getId();
+    method public int getIssueActionability();
     method public int getIssueCategory();
     method @NonNull public String getIssueTypeId();
+    method public int getNotificationBehavior();
     method @Nullable public android.app.PendingIntent getOnDismissPendingIntent();
     method public int getSeverityLevel();
     method @Nullable public CharSequence getSubtitle();
@@ -357,9 +392,19 @@
     method @NonNull public CharSequence getTitle();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.safetycenter.SafetySourceIssue> CREATOR;
+    field public static final int ISSUE_ACTIONABILITY_AUTOMATIC = 200; // 0xc8
+    field public static final int ISSUE_ACTIONABILITY_MANUAL = 0; // 0x0
+    field public static final int ISSUE_ACTIONABILITY_TIP = 100; // 0x64
     field public static final int ISSUE_CATEGORY_ACCOUNT = 200; // 0xc8
+    field public static final int ISSUE_CATEGORY_DATA = 400; // 0x190
     field public static final int ISSUE_CATEGORY_DEVICE = 100; // 0x64
     field public static final int ISSUE_CATEGORY_GENERAL = 300; // 0x12c
+    field public static final int ISSUE_CATEGORY_PASSWORDS = 500; // 0x1f4
+    field public static final int ISSUE_CATEGORY_PERSONAL_SAFETY = 600; // 0x258
+    field public static final int NOTIFICATION_BEHAVIOR_DELAYED = 200; // 0xc8
+    field public static final int NOTIFICATION_BEHAVIOR_IMMEDIATELY = 300; // 0x12c
+    field public static final int NOTIFICATION_BEHAVIOR_NEVER = 100; // 0x64
+    field public static final int NOTIFICATION_BEHAVIOR_UNSPECIFIED = 0; // 0x0
   }
 
   public static final class SafetySourceIssue.Action implements android.os.Parcelable {
@@ -375,6 +420,7 @@
 
   public static final class SafetySourceIssue.Action.Builder {
     ctor public SafetySourceIssue.Action.Builder(@NonNull String, @NonNull CharSequence, @NonNull android.app.PendingIntent);
+    ctor public SafetySourceIssue.Action.Builder(@NonNull android.safetycenter.SafetySourceIssue.Action);
     method @NonNull public android.safetycenter.SafetySourceIssue.Action build();
     method @NonNull public android.safetycenter.SafetySourceIssue.Action.Builder setSuccessMessage(@Nullable CharSequence);
     method @NonNull public android.safetycenter.SafetySourceIssue.Action.Builder setWillResolve(boolean);
@@ -382,14 +428,38 @@
 
   public static final class SafetySourceIssue.Builder {
     ctor public SafetySourceIssue.Builder(@NonNull String, @NonNull CharSequence, @NonNull CharSequence, int, @NonNull String);
+    ctor public SafetySourceIssue.Builder(@NonNull android.safetycenter.SafetySourceIssue);
     method @NonNull public android.safetycenter.SafetySourceIssue.Builder addAction(@NonNull android.safetycenter.SafetySourceIssue.Action);
     method @NonNull public android.safetycenter.SafetySourceIssue build();
     method @NonNull public android.safetycenter.SafetySourceIssue.Builder clearActions();
+    method @NonNull public android.safetycenter.SafetySourceIssue.Builder setAttributionTitle(@Nullable CharSequence);
+    method @NonNull public android.safetycenter.SafetySourceIssue.Builder setCustomNotification(@Nullable android.safetycenter.SafetySourceIssue.Notification);
+    method @NonNull public android.safetycenter.SafetySourceIssue.Builder setDeduplicationId(@Nullable String);
+    method @NonNull public android.safetycenter.SafetySourceIssue.Builder setIssueActionability(int);
     method @NonNull public android.safetycenter.SafetySourceIssue.Builder setIssueCategory(int);
+    method @NonNull public android.safetycenter.SafetySourceIssue.Builder setNotificationBehavior(int);
     method @NonNull public android.safetycenter.SafetySourceIssue.Builder setOnDismissPendingIntent(@Nullable android.app.PendingIntent);
     method @NonNull public android.safetycenter.SafetySourceIssue.Builder setSubtitle(@Nullable CharSequence);
   }
 
+  public static final class SafetySourceIssue.Notification implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.List<android.safetycenter.SafetySourceIssue.Action> getActions();
+    method @NonNull public CharSequence getText();
+    method @NonNull public CharSequence getTitle();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.safetycenter.SafetySourceIssue.Notification> CREATOR;
+  }
+
+  public static final class SafetySourceIssue.Notification.Builder {
+    ctor public SafetySourceIssue.Notification.Builder(@NonNull CharSequence, @NonNull CharSequence);
+    ctor public SafetySourceIssue.Notification.Builder(@NonNull android.safetycenter.SafetySourceIssue.Notification);
+    method @NonNull public android.safetycenter.SafetySourceIssue.Notification.Builder addAction(@NonNull android.safetycenter.SafetySourceIssue.Action);
+    method @NonNull public android.safetycenter.SafetySourceIssue.Notification build();
+    method @NonNull public android.safetycenter.SafetySourceIssue.Notification.Builder clearActions();
+    method @NonNull public android.safetycenter.SafetySourceIssue.Notification.Builder setActions(@NonNull java.util.List<android.safetycenter.SafetySourceIssue.Action>);
+  }
+
   public final class SafetySourceStatus implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.safetycenter.SafetySourceStatus.IconAction getIconAction();
@@ -404,6 +474,7 @@
 
   public static final class SafetySourceStatus.Builder {
     ctor public SafetySourceStatus.Builder(@NonNull CharSequence, @NonNull CharSequence, int);
+    ctor public SafetySourceStatus.Builder(@NonNull android.safetycenter.SafetySourceStatus);
     method @NonNull public android.safetycenter.SafetySourceStatus build();
     method @NonNull public android.safetycenter.SafetySourceStatus.Builder setEnabled(boolean);
     method @NonNull public android.safetycenter.SafetySourceStatus.Builder setIconAction(@Nullable android.safetycenter.SafetySourceStatus.IconAction);
@@ -434,16 +505,21 @@
 
   public static final class SafetyCenterConfig.Builder {
     ctor public SafetyCenterConfig.Builder();
+    ctor public SafetyCenterConfig.Builder(@NonNull android.safetycenter.config.SafetyCenterConfig);
     method @NonNull public android.safetycenter.config.SafetyCenterConfig.Builder addSafetySourcesGroup(@NonNull android.safetycenter.config.SafetySourcesGroup);
     method @NonNull public android.safetycenter.config.SafetyCenterConfig build();
   }
 
   public final class SafetySource implements android.os.Parcelable {
+    method public boolean areNotificationsAllowed();
     method public int describeContents();
+    method @Nullable public String getDeduplicationGroup();
     method @NonNull public String getId();
     method public int getInitialDisplayState();
     method @Nullable public String getIntentAction();
     method public int getMaxSeverityLevel();
+    method @Nullable public String getOptionalPackageName();
+    method @NonNull public java.util.Set<java.lang.String> getPackageCertificateHashes();
     method @NonNull public String getPackageName();
     method public int getProfile();
     method @StringRes public int getSearchTermsResId();
@@ -468,12 +544,16 @@
 
   public static final class SafetySource.Builder {
     ctor public SafetySource.Builder(int);
+    ctor public SafetySource.Builder(@NonNull android.safetycenter.config.SafetySource);
+    method @NonNull public android.safetycenter.config.SafetySource.Builder addPackageCertificateHash(@NonNull String);
     method @NonNull public android.safetycenter.config.SafetySource build();
+    method @NonNull public android.safetycenter.config.SafetySource.Builder setDeduplicationGroup(@Nullable String);
     method @NonNull public android.safetycenter.config.SafetySource.Builder setId(@Nullable String);
     method @NonNull public android.safetycenter.config.SafetySource.Builder setInitialDisplayState(int);
     method @NonNull public android.safetycenter.config.SafetySource.Builder setIntentAction(@Nullable String);
     method @NonNull public android.safetycenter.config.SafetySource.Builder setLoggingAllowed(boolean);
     method @NonNull public android.safetycenter.config.SafetySource.Builder setMaxSeverityLevel(int);
+    method @NonNull public android.safetycenter.config.SafetySource.Builder setNotificationsAllowed(boolean);
     method @NonNull public android.safetycenter.config.SafetySource.Builder setPackageName(@Nullable String);
     method @NonNull public android.safetycenter.config.SafetySource.Builder setProfile(int);
     method @NonNull public android.safetycenter.config.SafetySource.Builder setRefreshOnPageOpenAllowed(boolean);
@@ -493,21 +573,33 @@
     method public int getType();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.safetycenter.config.SafetySourcesGroup> CREATOR;
-    field public static final int SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE = 0; // 0x0
+    field @Deprecated public static final int SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE = 0; // 0x0
     field public static final int SAFETY_SOURCES_GROUP_TYPE_HIDDEN = 2; // 0x2
-    field public static final int SAFETY_SOURCES_GROUP_TYPE_RIGID = 1; // 0x1
+    field @Deprecated public static final int SAFETY_SOURCES_GROUP_TYPE_RIGID = 1; // 0x1
+    field public static final int SAFETY_SOURCES_GROUP_TYPE_STATEFUL = 0; // 0x0
+    field public static final int SAFETY_SOURCES_GROUP_TYPE_STATELESS = 1; // 0x1
     field public static final int STATELESS_ICON_TYPE_NONE = 0; // 0x0
     field public static final int STATELESS_ICON_TYPE_PRIVACY = 1; // 0x1
   }
 
   public static final class SafetySourcesGroup.Builder {
     ctor public SafetySourcesGroup.Builder();
+    ctor public SafetySourcesGroup.Builder(@NonNull android.safetycenter.config.SafetySourcesGroup);
     method @NonNull public android.safetycenter.config.SafetySourcesGroup.Builder addSafetySource(@NonNull android.safetycenter.config.SafetySource);
     method @NonNull public android.safetycenter.config.SafetySourcesGroup build();
     method @NonNull public android.safetycenter.config.SafetySourcesGroup.Builder setId(@Nullable String);
     method @NonNull public android.safetycenter.config.SafetySourcesGroup.Builder setStatelessIconType(int);
     method @NonNull public android.safetycenter.config.SafetySourcesGroup.Builder setSummaryResId(@StringRes int);
     method @NonNull public android.safetycenter.config.SafetySourcesGroup.Builder setTitleResId(@StringRes int);
+    method @NonNull public android.safetycenter.config.SafetySourcesGroup.Builder setType(int);
+  }
+
+}
+
+package android.safetylabel {
+
+  public final class SafetyLabelConstants {
+    field public static final String SAFETY_LABEL_CHANGE_NOTIFICATION_ENABLED = "safety_label_change_notifications_enabled";
   }
 
 }
diff --git a/framework-s/java/android/app/role/RoleManager.java b/framework-s/java/android/app/role/RoleManager.java
index bd88310..1b72073 100644
--- a/framework-s/java/android/app/role/RoleManager.java
+++ b/framework-s/java/android/app/role/RoleManager.java
@@ -163,6 +163,15 @@
             "android.app.role.DEVICE_POLICY_MANAGEMENT";
 
     /**
+     * The name of the financed device kiosk role.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ROLE_FINANCED_DEVICE_KIOSK =
+            "android.app.role.FINANCED_DEVICE_KIOSK";
+
+    /**
      * @hide
      */
     @IntDef(flag = true, value = { MANAGE_HOLDERS_FLAG_DONT_KILL_APP })
diff --git a/framework-s/java/android/safetycenter/ISafetyCenterManager.aidl b/framework-s/java/android/safetycenter/ISafetyCenterManager.aidl
index a00bfa1..3290c22 100644
--- a/framework-s/java/android/safetycenter/ISafetyCenterManager.aidl
+++ b/framework-s/java/android/safetycenter/ISafetyCenterManager.aidl
@@ -22,6 +22,7 @@
 import android.safetycenter.SafetySourceData;
 import android.safetycenter.SafetySourceErrorDetails;
 import android.safetycenter.config.SafetyCenterConfig;
+import java.util.List;
 
 /**
  * AIDL Interface for communicating with the Safety Center, which consolidates UI for security and
@@ -72,6 +73,12 @@
     /** Requests safety sources to set their latest SafetySourceData for Safety Center. */
     void refreshSafetySources(int refreshReason, int userId);
 
+    /**
+    * Requests a specific subset of safety sources to set their latest SafetySourceData for
+    * Safety Center.
+    */
+    void refreshSpecificSafetySources(int refreshReason, int userId, in List<String> safetySourceIds);
+
     /** Returns the current SafetyCenterConfig, if available. */
     SafetyCenterConfig getSafetyCenterConfig();
 
@@ -90,8 +97,7 @@
             int userId);
 
     /**
-     * Dismiss a Safety Center issue and prevent it from appearing in the Safety Center or affecting
-     * the overall safety status.
+     * Dismiss a Safety Center issue and prevent it affecting the overall safety status.
      */
     void dismissSafetyCenterIssue(String issueId, int userId);
 
diff --git a/framework-s/java/android/safetycenter/SafetyCenterData.java b/framework-s/java/android/safetycenter/SafetyCenterData.java
index 9a6e7f0..ebe6fdd 100644
--- a/framework-s/java/android/safetycenter/SafetyCenterData.java
+++ b/framework-s/java/android/safetycenter/SafetyCenterData.java
@@ -17,17 +17,21 @@
 package android.safetycenter;
 
 import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
 
 import static java.util.Collections.unmodifiableList;
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import androidx.annotation.RequiresApi;
 
+import com.android.modules.utils.build.SdkLevel;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -53,7 +57,32 @@
                             in.createTypedArrayList(SafetyCenterEntryOrGroup.CREATOR);
                     List<SafetyCenterStaticEntryGroup> staticEntryGroups =
                             in.createTypedArrayList(SafetyCenterStaticEntryGroup.CREATOR);
-                    return new SafetyCenterData(status, issues, entryOrGroups, staticEntryGroups);
+
+                    if (SdkLevel.isAtLeastU()) {
+                        List<SafetyCenterIssue> dismissedIssues =
+                                in.createTypedArrayList(SafetyCenterIssue.CREATOR);
+                        Bundle extras = in.readBundle(getClass().getClassLoader());
+                        SafetyCenterData.Builder builder = new SafetyCenterData.Builder(status);
+                        for (int i = 0; i < issues.size(); i++) {
+                            builder.addIssue(issues.get(i));
+                        }
+                        for (int i = 0; i < entryOrGroups.size(); i++) {
+                            builder.addEntryOrGroup(entryOrGroups.get(i));
+                        }
+                        for (int i = 0; i < staticEntryGroups.size(); i++) {
+                            builder.addStaticEntryGroup(staticEntryGroups.get(i));
+                        }
+                        for (int i = 0; i < dismissedIssues.size(); i++) {
+                            builder.addDismissedIssue(dismissedIssues.get(i));
+                        }
+                        if (extras != null) {
+                            builder.setExtras(extras);
+                        }
+                        return builder.build();
+                    } else {
+                        return new SafetyCenterData(
+                                status, issues, entryOrGroups, staticEntryGroups);
+                    }
                 }
 
                 @Override
@@ -66,6 +95,8 @@
     @NonNull private final List<SafetyCenterIssue> mIssues;
     @NonNull private final List<SafetyCenterEntryOrGroup> mEntriesOrGroups;
     @NonNull private final List<SafetyCenterStaticEntryGroup> mStaticEntryGroups;
+    @NonNull private final List<SafetyCenterIssue> mDismissedIssues;
+    @NonNull private final Bundle mExtras;
 
     /** Creates a {@link SafetyCenterData}. */
     public SafetyCenterData(
@@ -77,6 +108,23 @@
         mIssues = unmodifiableList(new ArrayList<>(requireNonNull(issues)));
         mEntriesOrGroups = unmodifiableList(new ArrayList<>(requireNonNull(entriesOrGroups)));
         mStaticEntryGroups = unmodifiableList(new ArrayList<>(requireNonNull(staticEntryGroups)));
+        mDismissedIssues = unmodifiableList(new ArrayList<>());
+        mExtras = Bundle.EMPTY;
+    }
+
+    private SafetyCenterData(
+            @NonNull SafetyCenterStatus status,
+            @NonNull List<SafetyCenterIssue> issues,
+            @NonNull List<SafetyCenterEntryOrGroup> entriesOrGroups,
+            @NonNull List<SafetyCenterStaticEntryGroup> staticEntryGroups,
+            @NonNull List<SafetyCenterIssue> dismissedIssues,
+            @NonNull Bundle extras) {
+        mStatus = status;
+        mIssues = issues;
+        mEntriesOrGroups = entriesOrGroups;
+        mStaticEntryGroups = staticEntryGroups;
+        mDismissedIssues = dismissedIssues;
+        mExtras = extras;
     }
 
     /** Returns the overall {@link SafetyCenterStatus} of the Safety Center. */
@@ -106,6 +154,31 @@
         return mStaticEntryGroups;
     }
 
+    /** Returns the list of dismissed {@link SafetyCenterIssue} objects in the Safety Center. */
+    @NonNull
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public List<SafetyCenterIssue> getDismissedIssues() {
+        if (!SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException();
+        }
+        return mDismissedIssues;
+    }
+
+    /**
+     * Returns a {@link Bundle} containing additional information.
+     *
+     * <p>Note: internal state of this {@link Bundle} is not used for {@link Object#equals} and
+     * {@link Object#hashCode} implementation of {@link SafetyCenterData}.
+     */
+    @NonNull
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public Bundle getExtras() {
+        if (!SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException();
+        }
+        return mExtras;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -114,12 +187,14 @@
         return Objects.equals(mStatus, that.mStatus)
                 && Objects.equals(mIssues, that.mIssues)
                 && Objects.equals(mEntriesOrGroups, that.mEntriesOrGroups)
-                && Objects.equals(mStaticEntryGroups, that.mStaticEntryGroups);
+                && Objects.equals(mStaticEntryGroups, that.mStaticEntryGroups)
+                && Objects.equals(mDismissedIssues, that.mDismissedIssues);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mStatus, mIssues, mEntriesOrGroups, mStaticEntryGroups);
+        return Objects.hash(
+                mStatus, mIssues, mEntriesOrGroups, mStaticEntryGroups, mDismissedIssues);
     }
 
     @Override
@@ -133,6 +208,9 @@
                 + mEntriesOrGroups
                 + ", mStaticEntryGroups="
                 + mStaticEntryGroups
+                + ", mDismissedIssues="
+                + mDismissedIssues
+                + (!mExtras.isEmpty() ? ", (has extras)" : "")
                 + '}';
     }
 
@@ -147,5 +225,141 @@
         dest.writeTypedList(mIssues);
         dest.writeTypedList(mEntriesOrGroups);
         dest.writeTypedList(mStaticEntryGroups);
+        if (SdkLevel.isAtLeastU()) {
+            dest.writeTypedList(mDismissedIssues);
+            dest.writeBundle(mExtras);
+        }
+    }
+
+    /** Builder class for {@link SafetyCenterData}. */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public static final class Builder {
+
+        @NonNull private final SafetyCenterStatus mStatus;
+        @NonNull private final List<SafetyCenterIssue> mIssues = new ArrayList<>();
+        @NonNull private final List<SafetyCenterEntryOrGroup> mEntriesOrGroups = new ArrayList<>();
+
+        @NonNull
+        private final List<SafetyCenterStaticEntryGroup> mStaticEntryGroups = new ArrayList<>();
+
+        @NonNull private final List<SafetyCenterIssue> mDismissedIssues = new ArrayList<>();
+        @NonNull private Bundle mExtras = Bundle.EMPTY;
+
+        public Builder(@NonNull SafetyCenterStatus status) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            mStatus = requireNonNull(status);
+        }
+
+        /** Creates a {@link Builder} with the values from the given {@link SafetyCenterData}. */
+        public Builder(@NonNull SafetyCenterData safetyCenterData) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            requireNonNull(safetyCenterData);
+            mStatus = safetyCenterData.mStatus;
+            mIssues.addAll(safetyCenterData.mIssues);
+            mEntriesOrGroups.addAll(safetyCenterData.mEntriesOrGroups);
+            mStaticEntryGroups.addAll(safetyCenterData.mStaticEntryGroups);
+            mDismissedIssues.addAll(safetyCenterData.mDismissedIssues);
+            mExtras = safetyCenterData.mExtras.deepCopy();
+        }
+
+        /** Adds data for a {@link SafetyCenterIssue} to be shown in UI. */
+        @NonNull
+        public SafetyCenterData.Builder addIssue(@NonNull SafetyCenterIssue safetyCenterIssue) {
+            mIssues.add(requireNonNull(safetyCenterIssue));
+            return this;
+        }
+
+        /** Adds data for a {@link SafetyCenterEntryOrGroup} to be shown in UI. */
+        @NonNull
+        @SuppressWarnings("MissingGetterMatchingBuilder") // incorrectly expects "getEntryOrGroups"
+        public SafetyCenterData.Builder addEntryOrGroup(
+                @NonNull SafetyCenterEntryOrGroup safetyCenterEntryOrGroup) {
+            mEntriesOrGroups.add(requireNonNull(safetyCenterEntryOrGroup));
+            return this;
+        }
+
+        /** Adds data for a {@link SafetyCenterStaticEntryGroup} to be shown in UI. */
+        @NonNull
+        public SafetyCenterData.Builder addStaticEntryGroup(
+                @NonNull SafetyCenterStaticEntryGroup safetyCenterStaticEntryGroup) {
+            mStaticEntryGroups.add(requireNonNull(safetyCenterStaticEntryGroup));
+            return this;
+        }
+
+        /** Adds data for a dismissed {@link SafetyCenterIssue} to be shown in UI. */
+        @NonNull
+        public SafetyCenterData.Builder addDismissedIssue(
+                @NonNull SafetyCenterIssue dismissedSafetyCenterIssue) {
+            mDismissedIssues.add(requireNonNull(dismissedSafetyCenterIssue));
+            return this;
+        }
+
+        /** Sets additional information for the {@link SafetyCenterData}. */
+        @NonNull
+        public SafetyCenterData.Builder setExtras(@NonNull Bundle extras) {
+            mExtras = requireNonNull(extras);
+            return this;
+        }
+
+        /**
+         * Clears data for all the {@link SafetyCenterIssue}s that were added to this {@link
+         * SafetyCenterData.Builder}.
+         */
+        @NonNull
+        public SafetyCenterData.Builder clearIssues() {
+            mIssues.clear();
+            return this;
+        }
+
+        /**
+         * Clears data for all the {@link SafetyCenterEntryOrGroup}s that were added to this {@link
+         * SafetyCenterData.Builder}.
+         */
+        @NonNull
+        public SafetyCenterData.Builder clearEntriesOrGroups() {
+            mEntriesOrGroups.clear();
+            return this;
+        }
+
+        /**
+         * Clears data for all the {@link SafetyCenterStaticEntryGroup}s that were added to this
+         * {@link SafetyCenterData.Builder}.
+         */
+        @NonNull
+        public SafetyCenterData.Builder clearStaticEntryGroups() {
+            mStaticEntryGroups.clear();
+            return this;
+        }
+
+        /**
+         * Clears data for all the dismissed {@link SafetyCenterIssue}s that were added to this
+         * {@link SafetyCenterData.Builder}.
+         */
+        @NonNull
+        public SafetyCenterData.Builder clearDismissedIssues() {
+            mDismissedIssues.clear();
+            return this;
+        }
+
+        /**
+         * Creates the {@link SafetyCenterData} defined by this {@link SafetyCenterData.Builder}.
+         */
+        @NonNull
+        public SafetyCenterData build() {
+            List<SafetyCenterIssue> issues = unmodifiableList(new ArrayList<>(mIssues));
+            List<SafetyCenterEntryOrGroup> entriesOrGroups =
+                    unmodifiableList(new ArrayList<>(mEntriesOrGroups));
+            List<SafetyCenterStaticEntryGroup> staticEntryGroups =
+                    unmodifiableList(new ArrayList<>(mStaticEntryGroups));
+            List<SafetyCenterIssue> dismissedIssues =
+                    unmodifiableList(new ArrayList<>(mDismissedIssues));
+
+            return new SafetyCenterData(
+                    mStatus, issues, entriesOrGroups, staticEntryGroups, dismissedIssues, mExtras);
+        }
     }
 }
diff --git a/framework-s/java/android/safetycenter/SafetyCenterEntryGroup.java b/framework-s/java/android/safetycenter/SafetyCenterEntryGroup.java
index 5d34354..20d0260 100644
--- a/framework-s/java/android/safetycenter/SafetyCenterEntryGroup.java
+++ b/framework-s/java/android/safetycenter/SafetyCenterEntryGroup.java
@@ -26,6 +26,7 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.safetycenter.config.SafetySourcesGroup;
 import android.text.TextUtils;
 
 import androidx.annotation.RequiresApi;
@@ -86,10 +87,7 @@
         mEntries = entries;
     }
 
-    /**
-     * Returns the encoded string ID which uniquely identifies this entry group within the Safety
-     * Center on the device for the current user across all profiles and accounts.
-     */
+    /** Returns the ID of the {@link SafetySourcesGroup} that this group corresponds to. */
     @NonNull
     public String getId() {
         return mId;
diff --git a/framework-s/java/android/safetycenter/SafetyCenterIssue.java b/framework-s/java/android/safetycenter/SafetyCenterIssue.java
index 6d52e02..29df693 100644
--- a/framework-s/java/android/safetycenter/SafetyCenterIssue.java
+++ b/framework-s/java/android/safetycenter/SafetyCenterIssue.java
@@ -17,6 +17,7 @@
 package android.safetycenter;
 
 import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
 
 import static java.util.Collections.unmodifiableList;
 import static java.util.Objects.requireNonNull;
@@ -29,10 +30,13 @@
 import android.app.PendingIntent;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.safetycenter.config.SafetySourcesGroup;
 import android.text.TextUtils;
 
 import androidx.annotation.RequiresApi;
 
+import com.android.modules.utils.build.SdkLevel;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -91,13 +95,19 @@
                     CharSequence title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
                     CharSequence subtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
                     CharSequence summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
-                    return new Builder(id, title, summary)
-                            .setSubtitle(subtitle)
-                            .setSeverityLevel(in.readInt())
-                            .setDismissible(in.readBoolean())
-                            .setShouldConfirmDismissal(in.readBoolean())
-                            .setActions(in.createTypedArrayList(Action.CREATOR))
-                            .build();
+                    SafetyCenterIssue.Builder builder =
+                            new Builder(id, title, summary)
+                                    .setSubtitle(subtitle)
+                                    .setSeverityLevel(in.readInt())
+                                    .setDismissible(in.readBoolean())
+                                    .setShouldConfirmDismissal(in.readBoolean())
+                                    .setActions(in.createTypedArrayList(Action.CREATOR));
+                    if (SdkLevel.isAtLeastU()) {
+                        builder.setAttributionTitle(
+                                TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in));
+                        builder.setGroupId(in.readString());
+                    }
+                    return builder.build();
                 }
 
                 @Override
@@ -114,6 +124,8 @@
     private final boolean mDismissible;
     private final boolean mShouldConfirmDismissal;
     @NonNull private final List<Action> mActions;
+    @Nullable private final CharSequence mAttributionTitle;
+    @Nullable private final String mGroupId;
 
     private SafetyCenterIssue(
             @NonNull String id,
@@ -123,7 +135,9 @@
             @IssueSeverityLevel int severityLevel,
             boolean isDismissible,
             boolean shouldConfirmDismissal,
-            @NonNull List<Action> actions) {
+            @NonNull List<Action> actions,
+            @Nullable CharSequence attributionTitle,
+            @Nullable String groupId) {
         mId = id;
         mTitle = title;
         mSubtitle = subtitle;
@@ -132,6 +146,8 @@
         mDismissible = isDismissible;
         mShouldConfirmDismissal = shouldConfirmDismissal;
         mActions = actions;
+        mAttributionTitle = attributionTitle;
+        mGroupId = groupId;
     }
 
     /**
@@ -161,6 +177,24 @@
         return mSummary;
     }
 
+    /**
+     * Returns the attribution title of this issue, or {@code null} if it has none.
+     *
+     * <p>This is displayed in the UI and helps to attribute issue cards to a particular source.
+     *
+     * @throws UnsupportedOperationException if accessed from a version lower than {@link
+     *     UPSIDE_DOWN_CAKE}
+     */
+    @Nullable
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public CharSequence getAttributionTitle() {
+        if (!SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException(
+                    "Method not supported for versions lower than UPSIDE_DOWN_CAKE");
+        }
+        return mAttributionTitle;
+    }
+
     /** Returns the {@link IssueSeverityLevel} of this issue. */
     @IssueSeverityLevel
     public int getSeverityLevel() {
@@ -188,6 +222,26 @@
         return mActions;
     }
 
+    /**
+     * Returns the ID of the {@link SafetySourcesGroup} that this issue belongs to, or {@code null}
+     * if it has none.
+     *
+     * <p>This ID is used for displaying the issue on its corresponding subpage in the Safety Center
+     * UI.
+     *
+     * @throws UnsupportedOperationException if accessed from a version lower than {@link
+     *     UPSIDE_DOWN_CAKE}
+     */
+    @Nullable
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public String getGroupId() {
+        if (!SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException(
+                    "Method not supported for versions lower than UPSIDE_DOWN_CAKE");
+        }
+        return mGroupId;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -200,7 +254,9 @@
                 && TextUtils.equals(mTitle, that.mTitle)
                 && TextUtils.equals(mSubtitle, that.mSubtitle)
                 && TextUtils.equals(mSummary, that.mSummary)
-                && Objects.equals(mActions, that.mActions);
+                && Objects.equals(mActions, that.mActions)
+                && TextUtils.equals(mAttributionTitle, that.mAttributionTitle)
+                && Objects.equals(mGroupId, that.mGroupId);
     }
 
     @Override
@@ -213,7 +269,9 @@
                 mSeverityLevel,
                 mDismissible,
                 mShouldConfirmDismissal,
-                mActions);
+                mActions,
+                mAttributionTitle,
+                mGroupId);
     }
 
     @Override
@@ -235,6 +293,10 @@
                 + mShouldConfirmDismissal
                 + ", mActions="
                 + mActions
+                + ", mAttributionTitle="
+                + mAttributionTitle
+                + ", mGroupId="
+                + mGroupId
                 + '}';
     }
 
@@ -253,6 +315,10 @@
         dest.writeBoolean(mDismissible);
         dest.writeBoolean(mShouldConfirmDismissal);
         dest.writeTypedList(mActions);
+        if (SdkLevel.isAtLeastU()) {
+            TextUtils.writeToParcel(mAttributionTitle, dest, flags);
+            dest.writeString(mGroupId);
+        }
     }
 
     /** Builder class for {@link SafetyCenterIssue}. */
@@ -266,6 +332,8 @@
         private boolean mDismissible = true;
         private boolean mShouldConfirmDismissal = true;
         private List<Action> mActions = new ArrayList<>();
+        @Nullable private CharSequence mAttributionTitle;
+        @Nullable private String mGroupId;
 
         /**
          * Creates a {@link Builder} for a {@link SafetyCenterIssue}.
@@ -291,6 +359,8 @@
             mDismissible = issue.mDismissible;
             mShouldConfirmDismissal = issue.mShouldConfirmDismissal;
             mActions = new ArrayList<>(issue.mActions);
+            mAttributionTitle = issue.mAttributionTitle;
+            mGroupId = issue.mGroupId;
         }
 
         /** Sets the ID for this issue. */
@@ -322,6 +392,25 @@
         }
 
         /**
+         * Sets or clears the optional attribution title for this issue.
+         *
+         * <p>This is displayed in the UI and helps to attribute issue cards to a particular source.
+         *
+         * @throws UnsupportedOperationException if accessed from a version lower than {@link
+         *     UPSIDE_DOWN_CAKE}
+         */
+        @NonNull
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder setAttributionTitle(@Nullable CharSequence attributionTitle) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException(
+                        "Method not supported for versions lower than UPSIDE_DOWN_CAKE");
+            }
+            mAttributionTitle = attributionTitle;
+            return this;
+        }
+
+        /**
          * Sets {@link IssueSeverityLevel} for this issue. Defaults to {@link
          * #ISSUE_SEVERITY_LEVEL_OK}.
          */
@@ -357,6 +446,27 @@
             return this;
         }
 
+        /**
+         * Sets the ID of {@link SafetySourcesGroup} that this issue belongs to. Defaults to a
+         * {@code null} value.
+         *
+         * <p>This ID is used for displaying the issue on its corresponding subpage in the Safety
+         * Center UI.
+         *
+         * @throws UnsupportedOperationException if accessed from a version lower than {@link
+         *     UPSIDE_DOWN_CAKE}
+         */
+        @NonNull
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder setGroupId(@Nullable String groupId) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException(
+                        "Method not supported for versions lower than UPSIDE_DOWN_CAKE");
+            }
+            mGroupId = groupId;
+            return this;
+        }
+
         /** Creates the {@link SafetyCenterIssue} defined by this {@link Builder}. */
         @NonNull
         public SafetyCenterIssue build() {
@@ -368,7 +478,9 @@
                     mSeverityLevel,
                     mDismissible,
                     mShouldConfirmDismissal,
-                    unmodifiableList(new ArrayList<>(mActions)));
+                    unmodifiableList(new ArrayList<>(mActions)),
+                    mAttributionTitle,
+                    mGroupId);
         }
     }
 
@@ -547,6 +659,21 @@
                 mPendingIntent = requireNonNull(pendingIntent);
             }
 
+            /** Creates a {@link Builder} with the values from the given {@link Action}. */
+            @RequiresApi(UPSIDE_DOWN_CAKE)
+            public Builder(@NonNull Action action) {
+                if (!SdkLevel.isAtLeastU()) {
+                    throw new UnsupportedOperationException();
+                }
+                requireNonNull(action);
+                mId = action.mId;
+                mLabel = action.mLabel;
+                mPendingIntent = action.mPendingIntent;
+                mWillResolve = action.mWillResolve;
+                mInFlight = action.mInFlight;
+                mSuccessMessage = action.mSuccessMessage;
+            }
+
             /** Sets the ID of this {@link Action} */
             @NonNull
             public Builder setId(@NonNull String id) {
diff --git a/framework-s/java/android/safetycenter/SafetyCenterManager.java b/framework-s/java/android/safetycenter/SafetyCenterManager.java
index acee611..bb67f57 100644
--- a/framework-s/java/android/safetycenter/SafetyCenterManager.java
+++ b/framework-s/java/android/safetycenter/SafetyCenterManager.java
@@ -21,6 +21,7 @@
 import static android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE;
 import static android.annotation.SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION;
 import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
 
 import static java.util.Objects.requireNonNull;
 
@@ -32,6 +33,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
@@ -42,9 +44,11 @@
 import androidx.annotation.RequiresApi;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.modules.utils.build.SdkLevel;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Executor;
 
@@ -195,12 +199,21 @@
      * disambiguate personal profile vs. managed profiles issues).
      *
      * <p>This extra can be used in conjunction with {@link #EXTRA_SAFETY_SOURCE_ISSUE_ID} and
-     * {@link #EXTRA_SAFETY_SOURCE_ID}. Otherwise, no redirection will occur.
+     * {@link #EXTRA_SAFETY_SOURCE_ID}. Otherwise, the device's primary user will be used.
      */
     public static final String EXTRA_SAFETY_SOURCE_USER_HANDLE =
             "android.safetycenter.extra.SAFETY_SOURCE_USER_HANDLE";
 
     /**
+     * Used as a {@code String} extra field in {@link Intent#ACTION_SAFETY_CENTER} intents to
+     * specify the ID for a group of safety sources. If applicable, this will redirect to the
+     * group's corresponding subpage in the UI.
+     */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public static final String EXTRA_SAFETY_SOURCES_GROUP_ID =
+            "android.safetycenter.extra.SAFETY_SOURCES_GROUP_ID";
+
+    /**
      * Used as an int value for {@link #EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE} to indicate that
      * the safety source should fetch fresh data relating to their safety state upon receiving a
      * broadcast with intent action {@link #ACTION_REFRESH_SAFETY_SOURCES} and provide it to Safety
@@ -214,7 +227,7 @@
 
     /**
      * Used as an int value for {@link #EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE} to indicate that
-     * upon receiving a broadcasts with intent action {@link #ACTION_REFRESH_SAFETY_SOURCES}, the
+     * upon receiving a broadcast with intent action {@link #ACTION_REFRESH_SAFETY_SOURCES}, the
      * safety source should provide data relating to their safety state to Safety Center.
      *
      * <p>If the source already has its safety data cached, it may provide it without triggering a
@@ -255,6 +268,10 @@
     /** Indicates a generic reason for Safety Center refresh. */
     public static final int REFRESH_REASON_OTHER = 600;
 
+    /** Indicates a periodic background refresh. */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public static final int REFRESH_REASON_PERIODIC = 700;
+
     /**
      * The reason for requesting a refresh of {@link SafetySourceData} from safety sources.
      *
@@ -268,9 +285,11 @@
                 REFRESH_REASON_DEVICE_REBOOT,
                 REFRESH_REASON_DEVICE_LOCALE_CHANGE,
                 REFRESH_REASON_SAFETY_CENTER_ENABLED,
-                REFRESH_REASON_OTHER
+                REFRESH_REASON_OTHER,
+                REFRESH_REASON_PERIODIC
             })
     @Retention(RetentionPolicy.SOURCE)
+    @TargetApi(UPSIDE_DOWN_CAKE)
     public @interface RefreshReason {}
 
     /** Listener for changes to {@link SafetyCenterData}. */
@@ -429,6 +448,42 @@
         }
     }
 
+    /**
+     * Requests a specific subset of safety sources to set their latest {@link SafetySourceData} for
+     * Safety Center.
+     *
+     * <p>This API sends a broadcast to safety sources with action {@link
+     * #ACTION_REFRESH_SAFETY_SOURCES} and {@link #EXTRA_REFRESH_SAFETY_SOURCE_IDS} to specify the
+     * IDs of safety sources being requested for data by Safety Center.
+     *
+     * <p>This API is an overload of {@link #refreshSafetySources(int)} and is used to request data
+     * from safety sources that are part of a subpage in the Safety Center UI.
+     *
+     * @see #refreshSafetySources(int)
+     * @param refreshReason the reason for the refresh
+     * @param safetySourceIds list of IDs for the safety sources being refreshed
+     * @throws UnsupportedOperationException if accessed from a version lower than {@link
+     *     UPSIDE_DOWN_CAKE}
+     */
+    @RequiresPermission(MANAGE_SAFETY_CENTER)
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public void refreshSafetySources(
+            @RefreshReason int refreshReason, @NonNull List<String> safetySourceIds) {
+        if (!SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException(
+                    "Method not supported for versions lower than UPSIDE_DOWN_CAKE");
+        }
+
+        requireNonNull(safetySourceIds, "safetySourceIds cannot be null");
+
+        try {
+            mService.refreshSpecificSafetySources(
+                    refreshReason, mContext.getUser().getIdentifier(), safetySourceIds);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** Returns the current {@link SafetyCenterConfig}, if available. */
     @RequiresPermission(MANAGE_SAFETY_CENTER)
     @Nullable
@@ -507,8 +562,7 @@
     }
 
     /**
-     * Dismiss a Safety Center issue and prevent it from appearing in the Safety Center or affecting
-     * the overall safety status.
+     * Dismiss a Safety Center issue and prevent it from affecting the overall safety status.
      *
      * @param safetyCenterIssueId the target issue ID returned by {@link SafetyCenterIssue#getId()}
      */
diff --git a/framework-s/java/android/safetycenter/SafetyEvent.java b/framework-s/java/android/safetycenter/SafetyEvent.java
index 694274f..72e8def 100644
--- a/framework-s/java/android/safetycenter/SafetyEvent.java
+++ b/framework-s/java/android/safetycenter/SafetyEvent.java
@@ -17,6 +17,9 @@
 package android.safetycenter;
 
 import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+
+import static java.util.Objects.requireNonNull;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -27,6 +30,8 @@
 
 import androidx.annotation.RequiresApi;
 
+import com.android.modules.utils.build.SdkLevel;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
@@ -231,6 +236,19 @@
             mType = validateType(type);
         }
 
+        /** Creates a {@link Builder} with the values from the given {@link SafetyEvent}. */
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder(@NonNull SafetyEvent safetyEvent) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            requireNonNull(safetyEvent);
+            mType = safetyEvent.mType;
+            mRefreshBroadcastId = safetyEvent.mRefreshBroadcastId;
+            mSafetySourceIssueId = safetyEvent.mSafetySourceIssueId;
+            mSafetySourceIssueActionId = safetyEvent.mSafetySourceIssueActionId;
+        }
+
         /**
          * Sets an optional broadcast id provided by Safety Center when requesting a refresh,
          * through {@link SafetyCenterManager#EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID}.
diff --git a/framework-s/java/android/safetycenter/SafetySourceData.java b/framework-s/java/android/safetycenter/SafetySourceData.java
index cba2fcd..1ac4a25 100644
--- a/framework-s/java/android/safetycenter/SafetySourceData.java
+++ b/framework-s/java/android/safetycenter/SafetySourceData.java
@@ -17,6 +17,7 @@
 package android.safetycenter;
 
 import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
 
 import static com.android.internal.util.Preconditions.checkArgument;
 
@@ -27,11 +28,14 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import androidx.annotation.RequiresApi;
 
+import com.android.modules.utils.build.SdkLevel;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -162,6 +166,12 @@
                     for (int i = 0; i < issues.size(); i++) {
                         builder.addIssue(issues.get(i));
                     }
+                    if (SdkLevel.isAtLeastU()) {
+                        Bundle extras = in.readBundle(getClass().getClassLoader());
+                        if (extras != null) {
+                            builder.setExtras(extras);
+                        }
+                    }
                     return builder.build();
                 }
 
@@ -173,11 +183,15 @@
 
     @Nullable private final SafetySourceStatus mStatus;
     @NonNull private final List<SafetySourceIssue> mIssues;
+    @NonNull private final Bundle mExtras;
 
     private SafetySourceData(
-            @Nullable SafetySourceStatus status, @NonNull List<SafetySourceIssue> issues) {
+            @Nullable SafetySourceStatus status,
+            @NonNull List<SafetySourceIssue> issues,
+            @NonNull Bundle extras) {
         this.mStatus = status;
         this.mIssues = issues;
+        this.mExtras = extras;
     }
 
     /** Returns the data for the {@link SafetySourceStatus} to be shown in UI. */
@@ -192,6 +206,21 @@
         return mIssues;
     }
 
+    /**
+     * Returns a {@link Bundle} containing additional information.
+     *
+     * <p>Note: internal state of this {@link Bundle} is not used for {@link Object#equals} and
+     * {@link Object#hashCode} implementation of {@link SafetySourceData}.
+     */
+    @NonNull
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public Bundle getExtras() {
+        if (!SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException();
+        }
+        return mExtras;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -201,6 +230,9 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeTypedObject(mStatus, flags);
         dest.writeTypedList(mIssues);
+        if (SdkLevel.isAtLeastU()) {
+            dest.writeBundle(mExtras);
+        }
     }
 
     @Override
@@ -218,7 +250,13 @@
 
     @Override
     public String toString() {
-        return "SafetySourceData{" + ", mStatus=" + mStatus + ", mIssues=" + mIssues + '}';
+        return "SafetySourceData{"
+                + "mStatus="
+                + mStatus
+                + ", mIssues="
+                + mIssues
+                + (!mExtras.isEmpty() ? ", (has extras)" : "")
+                + '}';
     }
 
     /** Builder class for {@link SafetySourceData}. */
@@ -227,6 +265,22 @@
         @NonNull private final List<SafetySourceIssue> mIssues = new ArrayList<>();
 
         @Nullable private SafetySourceStatus mStatus;
+        @NonNull private Bundle mExtras = Bundle.EMPTY;
+
+        /** Creates a {@link Builder} for a {@link SafetySourceData}. */
+        public Builder() {}
+
+        /** Creates a {@link Builder} with the values from the given {@link SafetySourceData}. */
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder(@NonNull SafetySourceData safetySourceData) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            requireNonNull(safetySourceData);
+            mIssues.addAll(safetySourceData.mIssues);
+            mStatus = safetySourceData.mStatus;
+            mExtras = safetySourceData.mExtras.deepCopy();
+        }
 
         /** Sets data for the {@link SafetySourceStatus} to be shown in UI. */
         @NonNull
@@ -242,6 +296,17 @@
             return this;
         }
 
+        /** Sets additional information for the {@link SafetySourceData}. */
+        @NonNull
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder setExtras(@NonNull Bundle extras) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            mExtras = requireNonNull(extras);
+            return this;
+        }
+
         /**
          * Clears data for all the {@link SafetySourceIssue}s that were added to this {@link
          * Builder}.
@@ -258,7 +323,7 @@
             List<SafetySourceIssue> issues = unmodifiableList(new ArrayList<>(mIssues));
             int issuesMaxSeverityLevel = getIssuesMaxSeverityLevelEnforcingUniqueIds(issues);
             if (mStatus == null) {
-                return new SafetySourceData(null, issues);
+                return new SafetySourceData(null, issues, mExtras);
             }
             int statusSeverityLevel = mStatus.getSeverityLevel();
             boolean requiresAttention = issuesMaxSeverityLevel > SEVERITY_LEVEL_INFORMATION;
@@ -268,7 +333,8 @@
                         "Safety source data cannot have issues that are more severe than its"
                                 + " status");
             }
-            return new SafetySourceData(mStatus, issues);
+
+            return new SafetySourceData(mStatus, issues, mExtras);
         }
 
         private static int getIssuesMaxSeverityLevelEnforcingUniqueIds(
diff --git a/framework-s/java/android/safetycenter/SafetySourceIssue.java b/framework-s/java/android/safetycenter/SafetySourceIssue.java
index cf64818..c4e2666 100644
--- a/framework-s/java/android/safetycenter/SafetySourceIssue.java
+++ b/framework-s/java/android/safetycenter/SafetySourceIssue.java
@@ -17,6 +17,7 @@
 package android.safetycenter;
 
 import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
 
 import static com.android.internal.util.Preconditions.checkArgument;
 
@@ -28,13 +29,17 @@
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
+import android.annotation.TargetApi;
 import android.app.PendingIntent;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
 
 import androidx.annotation.RequiresApi;
 
+import com.android.modules.utils.build.SdkLevel;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -64,9 +69,26 @@
     /** Indicates that the risk associated with the issue is related to a user's account safety. */
     public static final int ISSUE_CATEGORY_ACCOUNT = 200;
 
-    /** Indicates that the risk associated with the issue is related to a user's general safety. */
+    /**
+     * Indicates that the risk associated with the issue is related to a user's general safety.
+     *
+     * <p>This is the default. It is a generic value used when the category is not known or is not
+     * relevant.
+     */
     public static final int ISSUE_CATEGORY_GENERAL = 300;
 
+    /** Indicates that the risk associated with the issue is related to a user's data. */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public static final int ISSUE_CATEGORY_DATA = 400;
+
+    /** Indicates that the risk associated with the issue is related to a user's passwords. */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public static final int ISSUE_CATEGORY_PASSWORDS = 500;
+
+    /** Indicates that the risk associated with the issue is related to a user's personal safety. */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public static final int ISSUE_CATEGORY_PERSONAL_SAFETY = 600;
+
     /**
      * All possible issue categories.
      *
@@ -84,10 +106,108 @@
                 ISSUE_CATEGORY_DEVICE,
                 ISSUE_CATEGORY_ACCOUNT,
                 ISSUE_CATEGORY_GENERAL,
+                ISSUE_CATEGORY_DATA,
+                ISSUE_CATEGORY_PASSWORDS,
+                ISSUE_CATEGORY_PERSONAL_SAFETY
             })
     @Retention(RetentionPolicy.SOURCE)
+    @TargetApi(UPSIDE_DOWN_CAKE)
     public @interface IssueCategory {}
 
+    /** Value signifying that the source has not specified a particular notification behavior. */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public static final int NOTIFICATION_BEHAVIOR_UNSPECIFIED = 0;
+
+    /** An issue which Safety Center should never notify the user about. */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public static final int NOTIFICATION_BEHAVIOR_NEVER = 100;
+
+    /**
+     * An issue which Safety Center may notify the user about after a delay if it has not been
+     * resolved. Safety Center does not provide any guarantee about the duration of the delay.
+     */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public static final int NOTIFICATION_BEHAVIOR_DELAYED = 200;
+
+    /** An issue which Safety Center may notify the user about immediately. */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public static final int NOTIFICATION_BEHAVIOR_IMMEDIATELY = 300;
+
+    /**
+     * All possible notification behaviors.
+     *
+     * <p>The notification behavior of a {@link SafetySourceIssue} determines if and when Safety
+     * Center should notify the user about it.
+     *
+     * @hide
+     * @see Builder#setNotificationBehavior(int)
+     */
+    @IntDef(
+            prefix = {"NOTIFICATION_BEHAVIOR_"},
+            value = {
+                NOTIFICATION_BEHAVIOR_UNSPECIFIED,
+                NOTIFICATION_BEHAVIOR_NEVER,
+                NOTIFICATION_BEHAVIOR_DELAYED,
+                NOTIFICATION_BEHAVIOR_IMMEDIATELY
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    @TargetApi(UPSIDE_DOWN_CAKE)
+    public @interface NotificationBehavior {}
+
+    /**
+     * An issue which requires manual user input to be resolved.
+     *
+     * <p>This is the default.
+     */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public static final int ISSUE_ACTIONABILITY_MANUAL = 0;
+
+    /**
+     * An issue which is just a "tip" and may not require any user input.
+     *
+     * <p>It is still possible to provide {@link Action}s to e.g. "learn more" about it or
+     * acknowledge it.
+     */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public static final int ISSUE_ACTIONABILITY_TIP = 100;
+
+    /**
+     * An issue which has already been actioned and may not require any user input.
+     *
+     * <p>It is still possible to provide {@link Action}s to e.g. "learn more" about it or
+     * acknowledge it.
+     */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public static final int ISSUE_ACTIONABILITY_AUTOMATIC = 200;
+
+    /**
+     * All possible issue actionability.
+     *
+     * <p>An issue's actionability represent what action is expected from the user as a result of
+     * showing them this issue.
+     *
+     * <p>If the user needs to manually resolve it; this is typically achieved using an {@link
+     * Action} (e.g. by resolving the issue directly through the Safety Center screen, or by
+     * navigating to another page).
+     *
+     * <p>If the issue does not need to be resolved manually by the user, it is possible not to
+     * provide any {@link Action}. However, this may still be desirable to e.g. to "learn more"
+     * about it or acknowledge it.
+     *
+     * @hide
+     * @see Builder#setIssueActionability(int)
+     */
+    @IntDef(
+            prefix = {"ISSUE_ACTIONABILITY_"},
+            value = {
+                ISSUE_ACTIONABILITY_MANUAL,
+                ISSUE_ACTIONABILITY_TIP,
+                ISSUE_ACTIONABILITY_AUTOMATIC
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    @TargetApi(UPSIDE_DOWN_CAKE)
+    public @interface IssueActionability {}
+
     @NonNull
     public static final Creator<SafetySourceIssue> CREATOR =
             new Creator<SafetySourceIssue>() {
@@ -111,6 +231,14 @@
                     for (int i = 0; i < actions.size(); i++) {
                         builder.addAction(actions.get(i));
                     }
+                    if (SdkLevel.isAtLeastU()) {
+                        builder.setCustomNotification(in.readTypedObject(Notification.CREATOR));
+                        builder.setNotificationBehavior(in.readInt());
+                        builder.setAttributionTitle(
+                                TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in));
+                        builder.setDeduplicationId(in.readString());
+                        builder.setIssueActionability(in.readInt());
+                    }
                     return builder.build();
                 }
 
@@ -129,6 +257,11 @@
     @Nullable private final PendingIntent mOnDismissPendingIntent;
     @IssueCategory private final int mIssueCategory;
     @NonNull private final String mIssueTypeId;
+    @Nullable private final Notification mCustomNotification;
+    @NotificationBehavior private final int mNotificationBehavior;
+    @Nullable private final CharSequence mAttributionTitle;
+    @Nullable private final String mDeduplicationId;
+    @IssueActionability private final int mIssueActionability;
 
     private SafetySourceIssue(
             @NonNull String id,
@@ -139,7 +272,12 @@
             @IssueCategory int issueCategory,
             @NonNull List<Action> actions,
             @Nullable PendingIntent onDismissPendingIntent,
-            @NonNull String issueTypeId) {
+            @NonNull String issueTypeId,
+            @Nullable Notification customNotification,
+            @NotificationBehavior int notificationBehavior,
+            @Nullable CharSequence attributionTitle,
+            @Nullable String deduplicationId,
+            @IssueActionability int issueActionability) {
         this.mId = id;
         this.mTitle = title;
         this.mSubtitle = subtitle;
@@ -149,6 +287,11 @@
         this.mActions = actions;
         this.mOnDismissPendingIntent = onDismissPendingIntent;
         this.mIssueTypeId = issueTypeId;
+        this.mCustomNotification = customNotification;
+        this.mNotificationBehavior = notificationBehavior;
+        this.mAttributionTitle = attributionTitle;
+        this.mDeduplicationId = deduplicationId;
+        this.mIssueActionability = issueActionability;
     }
 
     /**
@@ -183,6 +326,22 @@
         return mSummary;
     }
 
+    /**
+     * Returns the localized attribution title of the issue to be displayed in the UI.
+     *
+     * <p>This is displayed in the UI and helps to attribute issue cards to a particular source. If
+     * this value is {@code null}, the title of the group that contains the Safety Source will be
+     * used.
+     */
+    @Nullable
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public CharSequence getAttributionTitle() {
+        if (!SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException();
+        }
+        return mAttributionTitle;
+    }
+
     /** Returns the {@link SafetySourceData.SeverityLevel} of the issue. */
     @SafetySourceData.SeverityLevel
     public int getSeverityLevel() {
@@ -243,6 +402,114 @@
         return mIssueTypeId;
     }
 
+    /**
+     * Returns the optional custom {@link Notification} for this issue which overrides the title,
+     * text and actions for any {@link android.app.Notification} generated for this {@link
+     * SafetySourceIssue}.
+     *
+     * <p>Safety Center may still generate a default notification from the other details of this
+     * issue when no custom notification has been set. See {@link #getNotificationBehavior()} for
+     * details
+     *
+     * @see Builder#setCustomNotification(android.safetycenter.SafetySourceIssue.Notification
+     * @see #getNotificationBehavior()
+     */
+    @Nullable
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public Notification getCustomNotification() {
+        if (!SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException();
+        }
+        return mCustomNotification;
+    }
+
+    /**
+     * Returns the {@link NotificationBehavior} for this issue which determines if and when Safety
+     * Center will post a notification for this issue.
+     *
+     * <p>Any notification will be based on the {@link #getCustomNotification()} if set, or the
+     * other properties of this issue otherwise.
+     *
+     * <ul>
+     *   <li>If {@link #NOTIFICATION_BEHAVIOR_IMMEDIATELY} then Safety Center will immediately
+     *       create and post a notification
+     *   <li>If {@link #NOTIFICATION_BEHAVIOR_DELAYED} then a notification will only be posted after
+     *       a delay, if this issue has not been resolved.
+     *   <li>If {@link #NOTIFICATION_BEHAVIOR_UNSPECIFIED} then a notification may or may not be
+     *       posted, the exact behavior is defined by Safety Center.
+     *   <li>If {@link #NOTIFICATION_BEHAVIOR_NEVER} Safety Center will never post a notification
+     *       about this issue. Sources should specify this behavior when they wish to handle their
+     *       own notifications. When this behavior is set sources should not set a custom
+     *       notification.
+     * </ul>
+     *
+     * @see Builder#setNotificationBehavior(int)
+     */
+    @NotificationBehavior
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public int getNotificationBehavior() {
+        if (!SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException();
+        }
+        return mNotificationBehavior;
+    }
+
+    /**
+     * Returns the identifier used to deduplicate this issue against other issues with the same
+     * deduplication identifiers.
+     *
+     * <p>Deduplication identifier will be used to identify duplicate issues. This identifier
+     * applies across all safety sources which are part of the same deduplication group.
+     * Deduplication groups can be set, for each source, in the SafetyCenter config. Therefore, two
+     * issues are considered duplicate if their sources are part of the same deduplication group and
+     * they have the same deduplication identifier.
+     *
+     * <p>Out of all issues that are found to be duplicates, only one will be shown in the UI (the
+     * one with the highest severity, or in case of same severities, the one placed highest in the
+     * config).
+     *
+     * <p>Expected usage implies different sources will coordinate to set the same deduplication
+     * identifiers on issues that they want to deduplicate.
+     *
+     * <p>This shouldn't be a default mechanism for deduplication of issues. Most of the time
+     * sources should coordinate or communicate to only send the issue from one of them. That would
+     * also allow sources to choose which one will be displaying the issue, instead of depending on
+     * severity and config order. This API should only be needed if for some reason this isn't
+     * possible, for example, when sources can't communicate with each other and/or send issues at
+     * different times and/or issues can be of different severities.
+     */
+    @Nullable
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public String getDeduplicationId() {
+        if (!SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException();
+        }
+        return mDeduplicationId;
+    }
+
+    /**
+     * Returns the {@link IssueActionability} for this issue which determines what type of action is
+     * required from the user:
+     *
+     * <ul>
+     *   <li>If {@link #ISSUE_ACTIONABILITY_MANUAL} then user input is required to resolve the issue
+     *   <li>If {@link #ISSUE_ACTIONABILITY_TIP} then the user needs to review this issue as a tip
+     *       to improve their overall safety, and possibly acknowledge it
+     *   <li>If {@link #ISSUE_ACTIONABILITY_AUTOMATIC} then the user needs to review this issue as
+     *       something that has been resolved on their behalf, and possibly acknowledge it
+     * </ul>
+     *
+     * @see Builder#setIssueActionability(int)
+     */
+    @IssueActionability
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public int getIssueActionability() {
+        if (!SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException();
+        }
+        return mIssueActionability;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -259,6 +526,13 @@
         dest.writeTypedList(mActions);
         dest.writeTypedObject(mOnDismissPendingIntent, flags);
         dest.writeString(mIssueTypeId);
+        if (SdkLevel.isAtLeastU()) {
+            dest.writeTypedObject(mCustomNotification, flags);
+            dest.writeInt(mNotificationBehavior);
+            TextUtils.writeToParcel(mAttributionTitle, dest, flags);
+            dest.writeString(mDeduplicationId);
+            dest.writeInt(mIssueActionability);
+        }
     }
 
     @Override
@@ -274,7 +548,12 @@
                 && mIssueCategory == that.mIssueCategory
                 && mActions.equals(that.mActions)
                 && Objects.equals(mOnDismissPendingIntent, that.mOnDismissPendingIntent)
-                && TextUtils.equals(mIssueTypeId, that.mIssueTypeId);
+                && TextUtils.equals(mIssueTypeId, that.mIssueTypeId)
+                && Objects.equals(mCustomNotification, that.mCustomNotification)
+                && mNotificationBehavior == that.mNotificationBehavior
+                && TextUtils.equals(mAttributionTitle, that.mAttributionTitle)
+                && TextUtils.equals(mDeduplicationId, that.mDeduplicationId)
+                && mIssueActionability == that.mIssueActionability;
     }
 
     @Override
@@ -288,7 +567,12 @@
                 mIssueCategory,
                 mActions,
                 mOnDismissPendingIntent,
-                mIssueTypeId);
+                mIssueTypeId,
+                mCustomNotification,
+                mNotificationBehavior,
+                mAttributionTitle,
+                mDeduplicationId,
+                mIssueActionability);
     }
 
     @Override
@@ -312,6 +596,16 @@
                 + mOnDismissPendingIntent
                 + ", mIssueTypeId="
                 + mIssueTypeId
+                + ", mCustomNotification="
+                + mCustomNotification
+                + ", mNotificationBehavior="
+                + mNotificationBehavior
+                + ", mAttributionTitle="
+                + mAttributionTitle
+                + ", mDeduplicationId="
+                + mDeduplicationId
+                + ", mIssueActionability="
+                + mIssueActionability
                 + '}';
     }
 
@@ -349,6 +643,18 @@
                     }
                 };
 
+        private static void enforceUniqueActionIds(
+                @NonNull List<SafetySourceIssue.Action> actions, @NonNull String message) {
+            Set<String> actionIds = new HashSet<>();
+            for (int i = 0; i < actions.size(); i++) {
+                SafetySourceIssue.Action action = actions.get(i);
+
+                String actionId = action.getId();
+                checkArgument(!actionIds.contains(actionId), message);
+                actionIds.add(actionId);
+            }
+        }
+
         @NonNull private final String mId;
         @NonNull private final CharSequence mLabel;
         @NonNull private final PendingIntent mPendingIntent;
@@ -481,6 +787,20 @@
                 mPendingIntent = requireNonNull(pendingIntent);
             }
 
+            /** Creates a {@link Builder} with the values from the given {@link Action}. */
+            @RequiresApi(UPSIDE_DOWN_CAKE)
+            public Builder(@NonNull Action action) {
+                if (!SdkLevel.isAtLeastU()) {
+                    throw new UnsupportedOperationException();
+                }
+                requireNonNull(action);
+                mId = action.mId;
+                mLabel = action.mLabel;
+                mPendingIntent = action.mPendingIntent;
+                mWillResolve = action.mWillResolve;
+                mSuccessMessage = action.mSuccessMessage;
+            }
+
             /**
              * Sets whether the action will resolve the safety issue. Defaults to {@code false}.
              *
@@ -511,6 +831,184 @@
         }
     }
 
+    /**
+     * Data for Safety Center to use when constructing a system {@link android.app.Notification}
+     * about a related {@link SafetySourceIssue}.
+     *
+     * <p>Safety Center can construct a default notification for any issue, but sources may use
+     * {@link Builder#setCustomNotification(android.safetycenter.SafetySourceIssue.Notification)} if
+     * they want to override the title, text or actions.
+     *
+     * @see #getCustomNotification()
+     * @see Builder#setCustomNotification(android.safetycenter.SafetySourceIssue.Notification)
+     * @see #getNotificationBehavior()
+     */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public static final class Notification implements Parcelable {
+
+        @NonNull
+        public static final Creator<Notification> CREATOR =
+                new Creator<Notification>() {
+                    @Override
+                    public Notification createFromParcel(Parcel in) {
+                        return new Builder(
+                                        TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in),
+                                        TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in))
+                                .setActions(in.createTypedArrayList(Action.CREATOR))
+                                .build();
+                    }
+
+                    @Override
+                    public Notification[] newArray(int size) {
+                        return new Notification[size];
+                    }
+                };
+
+        @NonNull private final CharSequence mTitle;
+        @NonNull private final CharSequence mText;
+        @NonNull private final List<Action> mActions;
+
+        private Notification(
+                @NonNull CharSequence title,
+                @NonNull CharSequence text,
+                @NonNull List<Action> actions) {
+            mTitle = title;
+            mText = text;
+            mActions = actions;
+        }
+
+        /**
+         * Custom title which will be used instead of {@link SafetySourceIssue#getTitle()} when
+         * building a {@link android.app.Notification} for this issue.
+         */
+        @NonNull
+        public CharSequence getTitle() {
+            return mTitle;
+        }
+
+        /**
+         * Custom text which will be used instead of {@link SafetySourceIssue#getSummary()} when
+         * building a {@link android.app.Notification} for this issue.
+         */
+        @NonNull
+        public CharSequence getText() {
+            return mText;
+        }
+
+        /**
+         * Custom list of {@link Action} instances which will be used instead of {@link
+         * SafetySourceIssue#getActions()} when building a {@link android.app.Notification} for this
+         * issue.
+         *
+         * <p>If this list is empty then the resulting {@link android.app.Notification} will have
+         * zero action buttons.
+         */
+        @NonNull
+        public List<Action> getActions() {
+            return mActions;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            TextUtils.writeToParcel(mTitle, dest, flags);
+            TextUtils.writeToParcel(mText, dest, flags);
+            dest.writeTypedList(mActions);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof Notification)) return false;
+            Notification that = (Notification) o;
+            return TextUtils.equals(mTitle, that.mTitle)
+                    && TextUtils.equals(mText, that.mText)
+                    && mActions.equals(that.mActions);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mTitle, mText, mActions);
+        }
+
+        @Override
+        public String toString() {
+            return "Notification{"
+                    + "mTitle="
+                    + mTitle
+                    + ", mText="
+                    + mText
+                    + ", mActions="
+                    + mActions
+                    + '}';
+        }
+
+        /** Builder for {@link SafetySourceIssue.Notification}. */
+        public static final class Builder {
+
+            @NonNull private final CharSequence mTitle;
+            @NonNull private final CharSequence mText;
+            @NonNull private final List<Action> mActions = new ArrayList<>();
+
+            public Builder(@NonNull CharSequence title, @NonNull CharSequence text) {
+                mTitle = requireNonNull(title);
+                mText = requireNonNull(text);
+            }
+
+            /** Creates a {@link Builder} with the values from the given {@link Notification}. */
+            public Builder(@NonNull Notification notification) {
+                requireNonNull(notification);
+                mTitle = notification.mTitle;
+                mText = notification.mText;
+                mActions.addAll(notification.mActions);
+            }
+
+            /** Adds an {@link Action} to be show on the custom {@link Notification}. */
+            @NonNull
+            public Builder addAction(@NonNull Action action) {
+                mActions.add(requireNonNull(action));
+                return this;
+            }
+
+            /**
+             * Sets the list of {@link Action}s to be show on the custom {@link Notification},
+             * removing any which were previously added.
+             */
+            @NonNull
+            public Builder setActions(@NonNull List<Action> actions) {
+                mActions.clear();
+                mActions.addAll(requireNonNull(actions));
+                return this;
+            }
+
+            /**
+             * Clears all the {@link Action}s that were added to this custom {@link
+             * Notification.Builder}.
+             */
+            @NonNull
+            public Builder clearActions() {
+                mActions.clear();
+                return this;
+            }
+
+            /** Builds a {@link Notification} instance. */
+            @NonNull
+            public Notification build() {
+                List<Action> actions = unmodifiableList(new ArrayList<>(mActions));
+                Action.enforceUniqueActionIds(
+                        actions, "Custom notification cannot have duplicate action ids");
+                checkArgument(
+                        actions.size() <= 2,
+                        "Custom notification must not contain more than 2 actions");
+                return new Notification(mTitle, mText, actions);
+            }
+        }
+    }
+
     /** Builder class for {@link SafetySourceIssue}. */
     public static final class Builder {
 
@@ -524,6 +1022,18 @@
         @Nullable private CharSequence mSubtitle;
         @IssueCategory private int mIssueCategory = ISSUE_CATEGORY_GENERAL;
         @Nullable private PendingIntent mOnDismissPendingIntent;
+        @Nullable private CharSequence mAttributionTitle;
+        @Nullable private String mDeduplicationId;
+
+        @Nullable private Notification mCustomNotification = null;
+
+        @SuppressLint("NewApi")
+        @NotificationBehavior
+        private int mNotificationBehavior = NOTIFICATION_BEHAVIOR_UNSPECIFIED;
+
+        @SuppressLint("NewApi")
+        @IssueActionability
+        private int mIssueActionability = ISSUE_ACTIONABILITY_MANUAL;
 
         /** Creates a {@link Builder} for a {@link SafetySourceIssue}. */
         public Builder(
@@ -539,6 +1049,29 @@
             this.mIssueTypeId = requireNonNull(issueTypeId);
         }
 
+        /** Creates a {@link Builder} with the values from the given {@link SafetySourceIssue}. */
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder(@NonNull SafetySourceIssue safetySourceIssue) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            requireNonNull(safetySourceIssue);
+            mId = safetySourceIssue.mId;
+            mTitle = safetySourceIssue.mTitle;
+            mSummary = safetySourceIssue.mSummary;
+            mSeverityLevel = safetySourceIssue.mSeverityLevel;
+            mIssueTypeId = safetySourceIssue.mIssueTypeId;
+            mActions.addAll(safetySourceIssue.mActions);
+            mSubtitle = safetySourceIssue.mSubtitle;
+            mIssueCategory = safetySourceIssue.mIssueCategory;
+            mOnDismissPendingIntent = safetySourceIssue.mOnDismissPendingIntent;
+            mAttributionTitle = safetySourceIssue.mAttributionTitle;
+            mDeduplicationId = safetySourceIssue.mDeduplicationId;
+            mCustomNotification = safetySourceIssue.mCustomNotification;
+            mNotificationBehavior = safetySourceIssue.mNotificationBehavior;
+            mIssueActionability = safetySourceIssue.mIssueActionability;
+        }
+
         /** Sets the localized subtitle. */
         @NonNull
         public Builder setSubtitle(@Nullable CharSequence subtitle) {
@@ -547,6 +1080,23 @@
         }
 
         /**
+         * Sets or clears the optional attribution title for this issue.
+         *
+         * <p>This is displayed in the UI and helps to attribute an issue to a particular source. If
+         * this value is {@code null}, the title of the group that contains the Safety Source will
+         * be used.
+         */
+        @NonNull
+        @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        public Builder setAttributionTitle(@Nullable CharSequence attributionTitle) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            mAttributionTitle = attributionTitle;
+            return this;
+        }
+
+        /**
          * Sets the category of the risk associated with the issue.
          *
          * <p>The default category will be {@link #ISSUE_CATEGORY_GENERAL}.
@@ -590,12 +1140,98 @@
             return this;
         }
 
+        /**
+         * Sets a custom {@link Notification} for this issue.
+         *
+         * <p>Using a custom {@link Notification} a source may specify a different {@link
+         * Notification#getTitle()}, {@link Notification#getText()} and {@link
+         * Notification#getActions()} for Safety Center to use when constructing a notification for
+         * this issue.
+         *
+         * <p>Safety Center may still generate a default notification from the other details of this
+         * issue when no custom notification has been set, depending on the issue's {@link
+         * #getNotificationBehavior()}.
+         *
+         * @see #getCustomNotification()
+         * @see #setNotificationBehavior(int)
+         */
+        @NonNull
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder setCustomNotification(@Nullable Notification customNotification) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            mCustomNotification = customNotification;
+            return this;
+        }
+
+        /**
+         * Sets the notification behavior of the issue.
+         *
+         * <p>Must be one of {@link #NOTIFICATION_BEHAVIOR_UNSPECIFIED}, {@link
+         * #NOTIFICATION_BEHAVIOR_NEVER}, {@link #NOTIFICATION_BEHAVIOR_DELAYED} or {@link
+         * #NOTIFICATION_BEHAVIOR_IMMEDIATELY}. See {@link #getNotificationBehavior()} for details
+         * of how Safety Center will interpret each of these.
+         *
+         * @see #getNotificationBehavior()
+         */
+        @NonNull
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder setNotificationBehavior(@NotificationBehavior int notificationBehavior) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            mNotificationBehavior = validateNotificationBehavior(notificationBehavior);
+            return this;
+        }
+
+        /**
+         * Sets the deduplication identifier for the issue.
+         *
+         * @see #getDeduplicationId()
+         */
+        @NonNull
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder setDeduplicationId(@Nullable String deduplicationId) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            mDeduplicationId = deduplicationId;
+            return this;
+        }
+
+        /**
+         * Sets the issue actionability of the issue.
+         *
+         * <p>Must be one of {@link #ISSUE_ACTIONABILITY_MANUAL} (default), {@link
+         * #ISSUE_ACTIONABILITY_TIP}, {@link #ISSUE_ACTIONABILITY_AUTOMATIC}.
+         *
+         * @see #getIssueActionability()
+         */
+        @NonNull
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder setIssueActionability(@IssueActionability int issueActionability) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            mIssueActionability = validateIssueActionability(issueActionability);
+            return this;
+        }
+
         /** Creates the {@link SafetySourceIssue} defined by this {@link Builder}. */
         @NonNull
         public SafetySourceIssue build() {
             List<SafetySourceIssue.Action> actions = unmodifiableList(new ArrayList<>(mActions));
-            enforceUniqueActionIds(actions);
-            checkArgument(!actions.isEmpty(), "Safety source issue must contain at least 1 action");
+            Action.enforceUniqueActionIds(
+                    actions, "Safety source issue cannot have duplicate action ids");
+            if (SdkLevel.isAtLeastU()) {
+                checkArgument(
+                        mIssueActionability != ISSUE_ACTIONABILITY_MANUAL || !actions.isEmpty(),
+                        "Actionable safety source issue must contain at least 1 action");
+            } else {
+                checkArgument(
+                        !actions.isEmpty(), "Safety source issue must contain at least 1 action");
+            }
             checkArgument(
                     actions.size() <= 2,
                     "Safety source issue must not contain more than 2 actions");
@@ -608,21 +1244,12 @@
                     mIssueCategory,
                     actions,
                     mOnDismissPendingIntent,
-                    mIssueTypeId);
-        }
-
-        private static void enforceUniqueActionIds(
-                @NonNull List<SafetySourceIssue.Action> actions) {
-            Set<String> actionIds = new HashSet<>();
-            for (int i = 0; i < actions.size(); i++) {
-                SafetySourceIssue.Action action = actions.get(i);
-
-                String actionId = action.getId();
-                checkArgument(
-                        !actionIds.contains(actionId),
-                        "Safety source issue cannot have duplicate action ids");
-                actionIds.add(actionId);
-            }
+                    mIssueTypeId,
+                    mCustomNotification,
+                    mNotificationBehavior,
+                    mAttributionTitle,
+                    mDeduplicationId,
+                    mIssueActionability);
         }
     }
 
@@ -652,7 +1279,43 @@
                 return value;
             default:
         }
+        if (SdkLevel.isAtLeastU()) {
+            switch (value) {
+                case ISSUE_CATEGORY_DATA:
+                case ISSUE_CATEGORY_PASSWORDS:
+                case ISSUE_CATEGORY_PERSONAL_SAFETY:
+                    return value;
+                default:
+            }
+        }
         throw new IllegalArgumentException(
                 "Unexpected IssueCategory for SafetySourceIssue: " + value);
     }
+
+    @NotificationBehavior
+    private static int validateNotificationBehavior(int value) {
+        switch (value) {
+            case NOTIFICATION_BEHAVIOR_UNSPECIFIED:
+            case NOTIFICATION_BEHAVIOR_NEVER:
+            case NOTIFICATION_BEHAVIOR_DELAYED:
+            case NOTIFICATION_BEHAVIOR_IMMEDIATELY:
+                return value;
+            default:
+        }
+        throw new IllegalArgumentException(
+                "Unexpected NotificationBehavior for SafetySourceIssue: " + value);
+    }
+
+    @IssueActionability
+    private static int validateIssueActionability(int value) {
+        switch (value) {
+            case ISSUE_ACTIONABILITY_MANUAL:
+            case ISSUE_ACTIONABILITY_TIP:
+            case ISSUE_ACTIONABILITY_AUTOMATIC:
+                return value;
+            default:
+        }
+        throw new IllegalArgumentException(
+                "Unexpected IssueActionability for SafetySourceIssue: " + value);
+    }
 }
diff --git a/framework-s/java/android/safetycenter/SafetySourceStatus.java b/framework-s/java/android/safetycenter/SafetySourceStatus.java
index b80fd39..37095eb 100644
--- a/framework-s/java/android/safetycenter/SafetySourceStatus.java
+++ b/framework-s/java/android/safetycenter/SafetySourceStatus.java
@@ -17,6 +17,7 @@
 package android.safetycenter;
 
 import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
 
 import static com.android.internal.util.Preconditions.checkArgument;
 
@@ -33,6 +34,8 @@
 
 import androidx.annotation.RequiresApi;
 
+import com.android.modules.utils.build.SdkLevel;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
@@ -341,6 +344,21 @@
             this.mSeverityLevel = validateSeverityLevel(severityLevel);
         }
 
+        /** Creates a {@link Builder} with the values of the given {@link SafetySourceStatus}. */
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder(@NonNull SafetySourceStatus safetySourceStatus) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            requireNonNull(safetySourceStatus);
+            mTitle = safetySourceStatus.mTitle;
+            mSummary = safetySourceStatus.mSummary;
+            mSeverityLevel = safetySourceStatus.mSeverityLevel;
+            mPendingIntent = safetySourceStatus.mPendingIntent;
+            mIconAction = safetySourceStatus.mIconAction;
+            mEnabled = safetySourceStatus.mEnabled;
+        }
+
         /**
          * Sets an optional {@link PendingIntent} for the safety source status.
          *
diff --git a/framework-s/java/android/safetycenter/TEST_MAPPING b/framework-s/java/android/safetycenter/TEST_MAPPING
index 5feb2e9..c702ee8 100644
--- a/framework-s/java/android/safetycenter/TEST_MAPPING
+++ b/framework-s/java/android/safetycenter/TEST_MAPPING
@@ -7,11 +7,22 @@
           "exclude-annotation": "com.android.bedstead.harrier.annotations.Postsubmit"
         }
       ]
+    },
+    {
+      "name": "SafetyCenterFunctionalTestCases",
+      "options": [
+        {
+          "exclude-annotation": "com.android.bedstead.harrier.annotations.Postsubmit"
+        }
+      ]
     }
   ],
   "postsubmit": [
     {
       "name": "CtsSafetyCenterTestCases"
+    },
+    {
+      "name": "SafetyCenterFunctionalTestCases"
     }
   ],
   "mainline-presubmit": [
diff --git a/framework-s/java/android/safetycenter/config/BuilderUtils.java b/framework-s/java/android/safetycenter/config/BuilderUtils.java
index e0e0818..b6ccced 100644
--- a/framework-s/java/android/safetycenter/config/BuilderUtils.java
+++ b/framework-s/java/android/safetycenter/config/BuilderUtils.java
@@ -25,7 +25,9 @@
 
 import androidx.annotation.RequiresApi;
 
+import java.util.Collection;
 import java.util.Objects;
+import java.util.regex.Pattern;
 
 @RequiresApi(TIRAMISU)
 final class BuilderUtils {
@@ -39,12 +41,12 @@
             boolean prohibited,
             @Nullable Object defaultValue) {
         if (attribute == null && required) {
-            throw new IllegalStateException("Required attribute " + name + " missing");
+            throwRequiredAttributeMissing(name);
         }
         boolean nonDefaultValueProvided = !Objects.equals(attribute, defaultValue);
         boolean checkProhibited = prohibited && nonDefaultValueProvided;
         if (attribute != null && checkProhibited) {
-            throw new IllegalStateException("Prohibited attribute " + name + " present");
+            throwProhibitedAttributePresent(name);
         }
     }
 
@@ -56,6 +58,17 @@
         validateAttribute(attribute, name, required, prohibited, null);
     }
 
+    static void validateId(
+            @Nullable String id,
+            @NonNull String name,
+            boolean required,
+            boolean prohibited) {
+        validateAttribute(id, name, required, prohibited, null);
+        if (!Pattern.compile("[0-9a-zA-Z_]+").matcher(id).matches()) {
+            throw new IllegalStateException("Attribute " + name + " invalid");
+        }
+    }
+
     @AnyRes
     static int validateResId(
             @Nullable @AnyRes Integer value,
@@ -67,7 +80,7 @@
             return Resources.ID_NULL;
         }
         if (required && value == Resources.ID_NULL) {
-            throw new IllegalStateException("Required attribute " + name + " invalid");
+            throwRequiredAttributeInvalid(name);
         }
         return value;
     }
@@ -119,4 +132,37 @@
         }
         return value;
     }
+
+    /**
+     * Validates a collection argument from a builder.
+     *
+     * <ul>
+     *   <li>If {@code required}, a non-empty collection must be supplied.
+     *   <li>If {@code prohibited}, an empty collection must be supplied.
+     * </ul>
+     */
+    static <T> void validateCollection(
+            @NonNull Collection<T> value,
+            @NonNull String name,
+            boolean required,
+            boolean prohibited) {
+        if (value.isEmpty() && required) {
+            throwRequiredAttributeMissing(name);
+        }
+        if (!value.isEmpty() && prohibited) {
+            throwProhibitedAttributePresent(name);
+        }
+    }
+
+    static void throwRequiredAttributeMissing(@NonNull String attribute) {
+        throw new IllegalStateException("Required attribute " + attribute + " missing");
+    }
+
+    static void throwProhibitedAttributePresent(@NonNull String attribute) {
+        throw new IllegalStateException("Prohibited attribute " + attribute + " present");
+    }
+
+    static void throwRequiredAttributeInvalid(@NonNull String attribute) {
+        throw new IllegalStateException("Required attribute " + attribute + " invalid");
+    }
 }
diff --git a/framework-s/java/android/safetycenter/config/SafetyCenterConfig.java b/framework-s/java/android/safetycenter/config/SafetyCenterConfig.java
index 9c8691e..c94cedb 100644
--- a/framework-s/java/android/safetycenter/config/SafetyCenterConfig.java
+++ b/framework-s/java/android/safetycenter/config/SafetyCenterConfig.java
@@ -17,6 +17,7 @@
 package android.safetycenter.config;
 
 import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
 
 import static java.util.Collections.unmodifiableList;
 import static java.util.Objects.requireNonNull;
@@ -28,6 +29,8 @@
 
 import androidx.annotation.RequiresApi;
 
+import com.android.modules.utils.build.SdkLevel;
+
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -115,6 +118,16 @@
         /** Creates a {@link Builder} for a {@link SafetyCenterConfig}. */
         public Builder() {}
 
+        /** Creates a {@link Builder} with the values from the given {@link SafetyCenterConfig}. */
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder(@NonNull SafetyCenterConfig safetyCenterConfig) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            requireNonNull(safetyCenterConfig);
+            mSafetySourcesGroups.addAll(safetyCenterConfig.mSafetySourcesGroups);
+        }
+
         /**
          * Adds a {@link SafetySourcesGroup} to the Safety Center configuration.
          *
@@ -129,8 +142,8 @@
         /**
          * Creates the {@link SafetyCenterConfig} defined by this {@link Builder}.
          *
-         * <p>Throws an {@link IllegalStateException} if any constraint on the Safety Center
-         * configuration is violated.
+         * @throws IllegalStateException if any constraint on the Safety Center configuration is
+         *     violated
          */
         @NonNull
         public SafetyCenterConfig build() {
diff --git a/framework-s/java/android/safetycenter/config/SafetySource.java b/framework-s/java/android/safetycenter/config/SafetySource.java
index e5008bf..fddb8b6 100644
--- a/framework-s/java/android/safetycenter/config/SafetySource.java
+++ b/framework-s/java/android/safetycenter/config/SafetySource.java
@@ -17,6 +17,9 @@
 package android.safetycenter.config;
 
 import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+
+import static java.util.Objects.requireNonNull;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -26,12 +29,17 @@
 import android.content.res.Resources;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.ArraySet;
 
 import androidx.annotation.RequiresApi;
 
+import com.android.modules.utils.build.SdkLevel;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Data class used to represent the initial configuration of a safety source.
@@ -152,20 +160,29 @@
                 @Override
                 public SafetySource createFromParcel(Parcel in) {
                     int type = in.readInt();
-                    return new Builder(type)
-                            .setId(in.readString())
-                            .setPackageName(in.readString())
-                            .setTitleResId(in.readInt())
-                            .setTitleForWorkResId(in.readInt())
-                            .setSummaryResId(in.readInt())
-                            .setIntentAction(in.readString())
-                            .setProfile(in.readInt())
-                            .setInitialDisplayState(in.readInt())
-                            .setMaxSeverityLevel(in.readInt())
-                            .setSearchTermsResId(in.readInt())
-                            .setLoggingAllowed(in.readBoolean())
-                            .setRefreshOnPageOpenAllowed(in.readBoolean())
-                            .build();
+                    Builder builder =
+                            new Builder(type)
+                                    .setId(in.readString())
+                                    .setPackageName(in.readString())
+                                    .setTitleResId(in.readInt())
+                                    .setTitleForWorkResId(in.readInt())
+                                    .setSummaryResId(in.readInt())
+                                    .setIntentAction(in.readString())
+                                    .setProfile(in.readInt())
+                                    .setInitialDisplayState(in.readInt())
+                                    .setMaxSeverityLevel(in.readInt())
+                                    .setSearchTermsResId(in.readInt())
+                                    .setLoggingAllowed(in.readBoolean())
+                                    .setRefreshOnPageOpenAllowed(in.readBoolean());
+                    if (SdkLevel.isAtLeastU()) {
+                        builder.setNotificationsAllowed(in.readBoolean());
+                        builder.setDeduplicationGroup(in.readString());
+                        List<String> certs = in.createStringArrayList();
+                        for (int i = 0; i < certs.size(); i++) {
+                            builder.addPackageCertificateHash(certs.get(i));
+                        }
+                    }
+                    return builder.build();
                 }
 
                 @Override
@@ -187,6 +204,9 @@
     @StringRes private final int mSearchTermsResId;
     private final boolean mLoggingAllowed;
     private final boolean mRefreshOnPageOpenAllowed;
+    private final boolean mNotificationsAllowed;
+    @Nullable final String mDeduplicationGroup;
+    @NonNull private final Set<String> mPackageCertificateHashes;
 
     private SafetySource(
             @SafetySourceType int type,
@@ -201,7 +221,10 @@
             int maxSeverityLevel,
             @StringRes int searchTermsResId,
             boolean loggingAllowed,
-            boolean refreshOnPageOpenAllowed) {
+            boolean refreshOnPageOpenAllowed,
+            boolean notificationsAllowed,
+            @Nullable String deduplicationGroup,
+            @NonNull Set<String> packageCertificateHashes) {
         mType = type;
         mId = id;
         mPackageName = packageName;
@@ -215,6 +238,9 @@
         mSearchTermsResId = searchTermsResId;
         mLoggingAllowed = loggingAllowed;
         mRefreshOnPageOpenAllowed = refreshOnPageOpenAllowed;
+        mNotificationsAllowed = notificationsAllowed;
+        mDeduplicationGroup = deduplicationGroup;
+        mPackageCertificateHashes = Set.copyOf(packageCertificateHashes);
     }
 
     /** Returns the type of this safety source. */
@@ -236,10 +262,14 @@
     /**
      * Returns the package name of this safety source.
      *
-     * <p>This is the package that owns the source. The package will receive refresh requests and it
-     * can send set requests for the source.
+     * <p>This is the package that owns the source. The package will receive refresh requests, and
+     * it can send set requests for the source. The package is also used to create an explicit
+     * pending intent from the intent action in the package context.
      *
-     * <p>Throws an {@link UnsupportedOperationException} if the source is of type static.
+     * @throws UnsupportedOperationException if the source is of type {@link
+     *     SafetySource#SAFETY_SOURCE_TYPE_STATIC} even if the optional package name field for the
+     *     source is set, for sources of type {@link SafetySource#SAFETY_SOURCE_TYPE_STATIC} use
+     *     {@link SafetySource#getOptionalPackageName()}
      */
     @NonNull
     public String getPackageName() {
@@ -251,13 +281,36 @@
     }
 
     /**
+     * Returns the package name of this safety source or null if undefined.
+     *
+     * <p>This is the package that owns the source.
+     *
+     * <p>The package is always defined for sources of type dynamic and issue-only. The package will
+     * receive refresh requests, and it can send set requests for sources of type dynamic and
+     * issue-only. The package is also used to create an explicit pending intent in the package
+     * context from the intent action if defined.
+     *
+     * <p>The package is optional for sources of type static. If present, the package is used to
+     * create an explicit pending intent in the package context from the intent action.
+     */
+    @Nullable
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public String getOptionalPackageName() {
+        if (!SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException();
+        }
+        return mPackageName;
+    }
+
+    /**
      * Returns the resource id of the title 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 is not provided.
      *
-     * <p>Throws an {@link UnsupportedOperationException} if the source is of type issue-only.
+     * @throws UnsupportedOperationException if the source is of type {@link
+     *     SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY}
      */
     @StringRes
     public int getTitleResId() {
@@ -275,8 +328,9 @@
      * 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 work is not provided.
      *
-     * <p>Throws an {@link UnsupportedOperationException} if the source is of type issue-only or if
-     * the profile property of the source is set to {@link SafetySource#PROFILE_PRIMARY}.
+     * @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}
      */
     @StringRes
     public int getTitleForWorkResId() {
@@ -298,7 +352,8 @@
      * 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 summary is not provided.
      *
-     * <p>Throws an {@link UnsupportedOperationException} if the source is of type issue-only.
+     * @throws UnsupportedOperationException if the source is of type {@link
+     *     SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY}
      */
     @StringRes
     public int getSummaryResId() {
@@ -316,7 +371,8 @@
      * source is displayed as an entry in the Safety Center page, and if the action is set to {@code
      * null} or if it does not resolve to an activity the source will be marked as disabled.
      *
-     * <p>Throws an {@link UnsupportedOperationException} if the source is of type issue-only.
+     * @throws UnsupportedOperationException if the source is of type {@link
+     *     SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY}
      */
     @Nullable
     public String getIntentAction() {
@@ -336,8 +392,9 @@
     /**
      * Returns the initial display state of this safety source.
      *
-     * <p>Throws an {@link UnsupportedOperationException} if the source is of type static or
-     * issue-only.
+     * @throws UnsupportedOperationException if the source is of type {@link
+     *     SafetySource#SAFETY_SOURCE_TYPE_STATIC} or {@link
+     *     SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY}
      */
     @InitialDisplayState
     public int getInitialDisplayState() {
@@ -361,7 +418,8 @@
      * android.safetycenter.SafetySourceData#SEVERITY_LEVEL_INFORMATION} even if the maximum
      * severity level is set to a lower value.
      *
-     * <p>Throws an {@link UnsupportedOperationException} if the source is of type static.
+     * @throws UnsupportedOperationException if the source is of type {@link
+     *     SafetySource#SAFETY_SOURCE_TYPE_STATIC}
      */
     public int getMaxSeverityLevel() {
         if (mType == SAFETY_SOURCE_TYPE_STATIC) {
@@ -378,7 +436,8 @@
      * 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 search terms are not provided.
      *
-     * <p>Throws an {@link UnsupportedOperationException} if the source is of type issue-only.
+     * @throws UnsupportedOperationException if the source is of type {@link
+     *     SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY}
      */
     @StringRes
     public int getSearchTermsResId() {
@@ -392,7 +451,8 @@
     /**
      * Returns the logging allowed property of this safety source.
      *
-     * <p>Throws an {@link UnsupportedOperationException} if the source is of type static.
+     * @throws UnsupportedOperationException if the source is of type {@link
+     *     SafetySource#SAFETY_SOURCE_TYPE_STATIC}
      */
     public boolean isLoggingAllowed() {
         if (mType == SAFETY_SOURCE_TYPE_STATIC) {
@@ -408,7 +468,8 @@
      * <p>If set to {@code true}, a refresh request will be sent to the source when the Safety
      * Center page is opened.
      *
-     * <p>Throws an {@link UnsupportedOperationException} if the source is of type static.
+     * @throws UnsupportedOperationException if the source is of type {@link
+     *     SafetySource#SAFETY_SOURCE_TYPE_STATIC}
      */
     public boolean isRefreshOnPageOpenAllowed() {
         if (mType == SAFETY_SOURCE_TYPE_STATIC) {
@@ -418,6 +479,57 @@
         return mRefreshOnPageOpenAllowed;
     }
 
+    /**
+     * Returns whether Safety Center may post Notifications about issues reported by this {@link
+     * SafetySource}.
+     *
+     * @see Builder#setNotificationsAllowed(boolean)
+     */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public boolean areNotificationsAllowed() {
+        if (!SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException();
+        }
+        return mNotificationsAllowed;
+    }
+
+    /**
+     * Returns the deduplication group this source belongs to.
+     *
+     * <p>Sources which are part of the same deduplication group can coordinate to deduplicate their
+     * issues.
+     */
+    @Nullable
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public String getDeduplicationGroup() {
+        if (!SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException();
+        }
+        return mDeduplicationGroup;
+    }
+
+    /**
+     * Returns a set of package certificate hashes representing valid signed packages that represent
+     * this {@link SafetySource}.
+     *
+     * <p>If one or more certificate hashes are set, Safety Center will validate that a package
+     * calling {@link android.safetycenter.SafetyCenterManager#setSafetySourceData} is signed with
+     * one of the certificates provided.
+     *
+     * <p>The default value is an empty {@code Set}, in which case only the package name is
+     * validated.
+     *
+     * @see Builder#addPackageCertificateHash(String)
+     */
+    @NonNull
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public Set<String> getPackageCertificateHashes() {
+        if (!SdkLevel.isAtLeastU()) {
+            throw new UnsupportedOperationException();
+        }
+        return mPackageCertificateHashes;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -435,7 +547,10 @@
                 && mMaxSeverityLevel == that.mMaxSeverityLevel
                 && mSearchTermsResId == that.mSearchTermsResId
                 && mLoggingAllowed == that.mLoggingAllowed
-                && mRefreshOnPageOpenAllowed == that.mRefreshOnPageOpenAllowed;
+                && mRefreshOnPageOpenAllowed == that.mRefreshOnPageOpenAllowed
+                && mNotificationsAllowed == that.mNotificationsAllowed
+                && Objects.equals(mDeduplicationGroup, that.mDeduplicationGroup)
+                && Objects.equals(mPackageCertificateHashes, that.mPackageCertificateHashes);
     }
 
     @Override
@@ -453,7 +568,10 @@
                 mMaxSeverityLevel,
                 mSearchTermsResId,
                 mLoggingAllowed,
-                mRefreshOnPageOpenAllowed);
+                mRefreshOnPageOpenAllowed,
+                mNotificationsAllowed,
+                mDeduplicationGroup,
+                mPackageCertificateHashes);
     }
 
     @Override
@@ -485,6 +603,12 @@
                 + mLoggingAllowed
                 + ", mRefreshOnPageOpenAllowed="
                 + mRefreshOnPageOpenAllowed
+                + ", mNotificationsAllowed="
+                + mNotificationsAllowed
+                + ", mDeduplicationGroup="
+                + mDeduplicationGroup
+                + ", mPackageCertificateHashes="
+                + mPackageCertificateHashes
                 + '}';
     }
 
@@ -508,6 +632,11 @@
         dest.writeInt(mSearchTermsResId);
         dest.writeBoolean(mLoggingAllowed);
         dest.writeBoolean(mRefreshOnPageOpenAllowed);
+        if (SdkLevel.isAtLeastU()) {
+            dest.writeBoolean(mNotificationsAllowed);
+            dest.writeString(mDeduplicationGroup);
+            dest.writeStringList(List.copyOf(mPackageCertificateHashes));
+        }
     }
 
     /** Builder class for {@link SafetySource}. */
@@ -526,12 +655,40 @@
         @Nullable @StringRes private Integer mSearchTermsResId;
         @Nullable private Boolean mLoggingAllowed;
         @Nullable private Boolean mRefreshOnPageOpenAllowed;
+        @Nullable private Boolean mNotificationsAllowed;
+        @Nullable private String mDeduplicationGroup;
+        @NonNull private final ArraySet<String> mPackageCertificateHashes = new ArraySet<>();
 
         /** Creates a {@link Builder} for a {@link SafetySource}. */
         public Builder(@SafetySourceType int type) {
             mType = type;
         }
 
+        /** Creates a {@link Builder} with the values from the given {@link SafetySource}. */
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder(@NonNull SafetySource safetySource) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            requireNonNull(safetySource);
+            mType = safetySource.mType;
+            mId = safetySource.mId;
+            mPackageName = safetySource.mPackageName;
+            mTitleResId = safetySource.mTitleResId;
+            mTitleForWorkResId = safetySource.mTitleForWorkResId;
+            mSummaryResId = safetySource.mSummaryResId;
+            mIntentAction = safetySource.mIntentAction;
+            mProfile = safetySource.mProfile;
+            mInitialDisplayState = safetySource.mInitialDisplayState;
+            mMaxSeverityLevel = safetySource.mMaxSeverityLevel;
+            mSearchTermsResId = safetySource.mSearchTermsResId;
+            mLoggingAllowed = safetySource.mLoggingAllowed;
+            mRefreshOnPageOpenAllowed = safetySource.mRefreshOnPageOpenAllowed;
+            mNotificationsAllowed = safetySource.mNotificationsAllowed;
+            mDeduplicationGroup = safetySource.mDeduplicationGroup;
+            mPackageCertificateHashes.addAll(safetySource.mPackageCertificateHashes);
+        }
+
         /**
          * Sets the id of this safety source.
          *
@@ -714,6 +871,61 @@
         }
 
         /**
+         * Sets the {@link #areNotificationsAllowed()} property of this {@link SafetySource}.
+         *
+         * <p>If set to {@code true} Safety Center may post Notifications about issues reported by
+         * this source.
+         *
+         * <p>The default value is {@code false}.
+         *
+         * @see #areNotificationsAllowed()
+         */
+        @NonNull
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder setNotificationsAllowed(boolean notificationsAllowed) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            mNotificationsAllowed = notificationsAllowed;
+            return this;
+        }
+
+        /**
+         * Sets the deduplication group for this source.
+         *
+         * <p>Sources which are part of the same deduplication group can coordinate to deduplicate
+         * issues that they're sending to SafetyCenter by providing the same deduplication
+         * identifier with those issues.
+         *
+         * <p>The deduplication group property is prohibited for sources of type static.
+         */
+        @NonNull
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder setDeduplicationGroup(@Nullable String deduplicationGroup) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            mDeduplicationGroup = deduplicationGroup;
+            return this;
+        }
+
+        /**
+         * Adds a package certificate hash to the {@link #getPackageCertificateHashes()} property of
+         * this {@link SafetySource}.
+         *
+         * @see #getPackageCertificateHashes()
+         */
+        @NonNull
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder addPackageCertificateHash(@NonNull String packageCertificateHash) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            mPackageCertificateHashes.add(packageCertificateHash);
+            return this;
+        }
+
+        /**
          * Creates the {@link SafetySource} defined by this {@link Builder}.
          *
          * <p>Throws an {@link IllegalStateException} if any constraint on the safety source is
@@ -721,19 +933,25 @@
          */
         @NonNull
         public SafetySource build() {
-            if (mType != SAFETY_SOURCE_TYPE_STATIC
-                    && mType != SAFETY_SOURCE_TYPE_DYNAMIC
-                    && mType != SAFETY_SOURCE_TYPE_ISSUE_ONLY) {
+            int type = mType;
+            if (type != SAFETY_SOURCE_TYPE_STATIC
+                    && type != SAFETY_SOURCE_TYPE_DYNAMIC
+                    && type != SAFETY_SOURCE_TYPE_ISSUE_ONLY) {
                 throw new IllegalStateException("Unexpected type");
             }
-            boolean isStatic = mType == SAFETY_SOURCE_TYPE_STATIC;
-            boolean isDynamic = mType == SAFETY_SOURCE_TYPE_DYNAMIC;
-            boolean isIssueOnly = mType == SAFETY_SOURCE_TYPE_ISSUE_ONLY;
+            boolean isStatic = type == SAFETY_SOURCE_TYPE_STATIC;
+            boolean isDynamic = type == SAFETY_SOURCE_TYPE_DYNAMIC;
+            boolean isIssueOnly = type == SAFETY_SOURCE_TYPE_ISSUE_ONLY;
 
-            BuilderUtils.validateAttribute(mId, "id", true, false);
+            String id = mId;
+            BuilderUtils.validateId(id, "id", true, false);
 
+            String packageName = mPackageName;
             BuilderUtils.validateAttribute(
-                    mPackageName, "packageName", isDynamic || isIssueOnly, isStatic);
+                    packageName,
+                    "packageName",
+                    isDynamic || isIssueOnly,
+                    isStatic && !SdkLevel.isAtLeastU());
 
             int initialDisplayState =
                     BuilderUtils.validateIntDef(
@@ -781,8 +999,9 @@
                     BuilderUtils.validateResId(
                             mSummaryResId, "summary", isDynamicNotHidden, isIssueOnly);
 
+            String intentAction = mIntentAction;
             BuilderUtils.validateAttribute(
-                    mIntentAction,
+                    intentAction,
                     "intentAction",
                     (isDynamic && isEnabled) || isStatic,
                     isIssueOnly);
@@ -807,20 +1026,41 @@
                             isStatic,
                             false);
 
+            String deduplicationGroup = mDeduplicationGroup;
+            boolean notificationsAllowed = false;
+            Set<String> packageCertificateHashes = Set.copyOf(mPackageCertificateHashes);
+            if (SdkLevel.isAtLeastU()) {
+                notificationsAllowed =
+                        BuilderUtils.validateBoolean(
+                                mNotificationsAllowed,
+                                "notificationsAllowed",
+                                false,
+                                isStatic,
+                                false);
+
+                BuilderUtils.validateAttribute(
+                        deduplicationGroup, "deduplicationGroup", false, isStatic);
+                BuilderUtils.validateCollection(
+                        packageCertificateHashes, "packageCertificateHashes", false, isStatic);
+            }
+
             return new SafetySource(
-                    mType,
-                    mId,
-                    mPackageName,
+                    type,
+                    id,
+                    packageName,
                     titleResId,
                     titleForWorkResId,
                     summaryResId,
-                    mIntentAction,
+                    intentAction,
                     profile,
                     initialDisplayState,
                     maxSeverityLevel,
                     searchTermsResId,
                     loggingAllowed,
-                    refreshOnPageOpenAllowed);
+                    refreshOnPageOpenAllowed,
+                    notificationsAllowed,
+                    deduplicationGroup,
+                    packageCertificateHashes);
         }
     }
 }
diff --git a/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java b/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java
index 2868ee7..2ff44fa 100644
--- a/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java
+++ b/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java
@@ -17,6 +17,7 @@
 package android.safetycenter.config;
 
 import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
 
 import static java.util.Collections.unmodifiableList;
 import static java.util.Objects.requireNonNull;
@@ -25,6 +26,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringRes;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.content.res.Resources;
 import android.os.Parcel;
@@ -32,6 +34,8 @@
 
 import androidx.annotation.RequiresApi;
 
+import com.android.modules.utils.build.SdkLevel;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -50,16 +54,39 @@
     /**
      * Indicates that the safety sources group should be displayed as a collapsible group with an
      * icon (stateless or stateful) and an optional default summary.
+     *
+     * @deprecated use {@link #SAFETY_SOURCES_GROUP_TYPE_STATEFUL} instead.
      */
     public static final int SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE = 0;
 
     /**
+     * Indicates that the safety sources group should be displayed as a group that may contribute to
+     * the overall Safety Center status. This is indicated by a group stateful icon. If all sources
+     * in the group have an unspecified status then a stateless group icon might be applied.
+     */
+    public static final int SAFETY_SOURCES_GROUP_TYPE_STATEFUL =
+            SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE;
+
+    /**
      * Indicates that the safety sources group should be displayed as a rigid group with no icon and
      * no summary.
+     *
+     * @deprecated use {@link #SAFETY_SOURCES_GROUP_TYPE_STATELESS} instead.
      */
     public static final int SAFETY_SOURCES_GROUP_TYPE_RIGID = 1;
 
-    /** Indicates that the safety sources group should not be displayed. */
+    /**
+     * Indicates that the safety sources group should be displayed as a group that does not
+     * contribute to the overall Safety Center status. All sources of type dynamic in the group can
+     * only report an unspecified status. The stateless icon and summary may be ignored and not be
+     * displayed.
+     */
+    public static final int SAFETY_SOURCES_GROUP_TYPE_STATELESS = SAFETY_SOURCES_GROUP_TYPE_RIGID;
+
+    /**
+     * Indicates that the safety sources group should not be displayed. All sources in the group
+     * must be of type issue-only.
+     */
     public static final int SAFETY_SOURCES_GROUP_TYPE_HIDDEN = 2;
 
     /**
@@ -67,24 +94,27 @@
      *
      * @hide
      */
+    @SuppressLint("UniqueConstants") // Intentionally renaming the COLLAPSIBLE and RIGID constants.
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(
             prefix = "SAFETY_SOURCES_GROUP_TYPE_",
             value = {
                 SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE,
+                SAFETY_SOURCES_GROUP_TYPE_STATEFUL,
                 SAFETY_SOURCES_GROUP_TYPE_RIGID,
+                SAFETY_SOURCES_GROUP_TYPE_STATELESS,
                 SAFETY_SOURCES_GROUP_TYPE_HIDDEN
             })
     public @interface SafetySourceGroupType {}
 
     /**
-     * Indicates that the safety sources group will not be displayed with any special icon when all
-     * the sources contained in it are stateless.
+     * Indicates that no special icon will be displayed by a safety sources group when all the
+     * sources contained in it are stateless.
      */
     public static final int STATELESS_ICON_TYPE_NONE = 0;
 
     /**
-     * Indicates that the safety sources group will be displayed with the privacy icon when all the
+     * Indicates that the privacy icon will be displayed by a safety sources group when all the
      * sources contained in it are stateless.
      */
     public static final int STATELESS_ICON_TYPE_PRIVACY = 1;
@@ -116,6 +146,9 @@
                     for (int i = 0; i < safetySources.size(); i++) {
                         builder.addSafetySource(safetySources.get(i));
                     }
+                    if (SdkLevel.isAtLeastU()) {
+                        builder.setType(in.readInt());
+                    }
                     return builder.build();
                 }
 
@@ -125,6 +158,7 @@
                 }
             };
 
+    @SafetySourceGroupType private final int mType;
     @NonNull private final String mId;
     @StringRes private final int mTitleResId;
     @StringRes private final int mSummaryResId;
@@ -132,11 +166,13 @@
     @NonNull private final List<SafetySource> mSafetySources;
 
     private SafetySourcesGroup(
+            @SafetySourceGroupType int type,
             @NonNull String id,
             @StringRes int titleResId,
             @StringRes int summaryResId,
             @StatelessIconType int statelessIconType,
             @NonNull List<SafetySource> safetySources) {
+        mType = type;
         mId = id;
         mTitleResId = titleResId;
         mSummaryResId = summaryResId;
@@ -144,23 +180,10 @@
         mSafetySources = safetySources;
     }
 
-    /**
-     * Returns the type of this safety sources group.
-     *
-     * <p>The type is inferred according to the state of certain fields. If no title is provided
-     * when building the group, the group is of type hidden. If a title is provided but no summary
-     * or stateless icon are provided when building the group, the group is of type rigid.
-     * Otherwise, the group is of type collapsible.
-     */
+    /** Returns the type of this safety sources group. */
     @SafetySourceGroupType
     public int getType() {
-        if (mTitleResId == Resources.ID_NULL) {
-            return SAFETY_SOURCES_GROUP_TYPE_HIDDEN;
-        }
-        if (mSummaryResId != Resources.ID_NULL || mStatelessIconType != STATELESS_ICON_TYPE_NONE) {
-            return SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE;
-        }
-        return SAFETY_SOURCES_GROUP_TYPE_RIGID;
+        return mType;
     }
 
     /**
@@ -224,7 +247,8 @@
         if (this == o) return true;
         if (!(o instanceof SafetySourcesGroup)) return false;
         SafetySourcesGroup that = (SafetySourcesGroup) o;
-        return Objects.equals(mId, that.mId)
+        return mType == that.mType
+                && Objects.equals(mId, that.mId)
                 && mTitleResId == that.mTitleResId
                 && mSummaryResId == that.mSummaryResId
                 && mStatelessIconType == that.mStatelessIconType
@@ -233,13 +257,16 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mId, mTitleResId, mSummaryResId, mStatelessIconType, mSafetySources);
+        return Objects.hash(
+                mType, mId, mTitleResId, mSummaryResId, mStatelessIconType, mSafetySources);
     }
 
     @Override
     public String toString() {
         return "SafetySourcesGroup{"
-                + "mId="
+                + "mType="
+                + mType
+                + ", mId="
                 + mId
                 + ", mTitleResId="
                 + mTitleResId
@@ -264,6 +291,9 @@
         dest.writeInt(mSummaryResId);
         dest.writeInt(mStatelessIconType);
         dest.writeTypedList(mSafetySources);
+        if (SdkLevel.isAtLeastU()) {
+            dest.writeInt(mType);
+        }
     }
 
     /** Builder class for {@link SafetySourcesGroup}. */
@@ -271,6 +301,7 @@
 
         private final List<SafetySource> mSafetySources = new ArrayList<>();
 
+        @Nullable @SafetySourceGroupType private Integer mType;
         @Nullable private String mId;
         @Nullable @StringRes private Integer mTitleResId;
         @Nullable @StringRes private Integer mSummaryResId;
@@ -279,6 +310,37 @@
         /** Creates a {@link Builder} for a {@link SafetySourcesGroup}. */
         public Builder() {}
 
+        /** Creates a {@link Builder} with the values from the given {@link SafetySourcesGroup}. */
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder(@NonNull SafetySourcesGroup original) {
+            if (!SdkLevel.isAtLeastU()) {
+                throw new UnsupportedOperationException();
+            }
+            requireNonNull(original);
+            mSafetySources.addAll(original.mSafetySources);
+            mType = original.mType;
+            mId = original.mId;
+            mTitleResId = original.mTitleResId;
+            mSummaryResId = original.mSummaryResId;
+            mStatelessIconType = original.mStatelessIconType;
+        }
+
+        /**
+         * Sets the type of this safety sources group.
+         *
+         * <p>If the type is not explicitly set, the type is inferred according to the state of
+         * certain fields. If no title is provided when building the group, the group is of type
+         * hidden. If a title is provided but no summary or stateless icon are provided when
+         * building the group, the group is of type stateless. Otherwise, the group is of type
+         * stateful.
+         */
+        @NonNull
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public Builder setType(@SafetySourceGroupType int type) {
+            mType = type;
+            return this;
+        }
+
         /**
          * Sets the id of this safety sources group.
          *
@@ -346,27 +408,20 @@
         /**
          * Creates the {@link SafetySourcesGroup} defined by this {@link Builder}.
          *
-         * <p>Throws an {@link IllegalStateException} if any constraint on the safety sources group
-         * is violated.
+         * @throws IllegalStateException if any constraint on the safety sources group is violated
          */
         @NonNull
         public SafetySourcesGroup build() {
-            BuilderUtils.validateAttribute(mId, "id", true, false);
+            String id = mId;
+            BuilderUtils.validateId(id, "id", true, false);
+
             List<SafetySource> safetySources = unmodifiableList(new ArrayList<>(mSafetySources));
             if (safetySources.isEmpty()) {
                 throw new IllegalStateException("Safety sources group empty");
             }
-            boolean titleRequired = false;
-            int safetySourcesSize = safetySources.size();
-            for (int i = 0; i < safetySourcesSize; i++) {
-                int type = safetySources.get(i).getType();
-                if (type != SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY) {
-                    titleRequired = true;
-                    break;
-                }
-            }
-            int titleResId = BuilderUtils.validateResId(mTitleResId, "title", titleRequired, false);
+
             int summaryResId = BuilderUtils.validateResId(mSummaryResId, "summary", false, false);
+
             int statelessIconType =
                     BuilderUtils.validateIntDef(
                             mStatelessIconType,
@@ -376,8 +431,53 @@
                             STATELESS_ICON_TYPE_NONE,
                             STATELESS_ICON_TYPE_NONE,
                             STATELESS_ICON_TYPE_PRIVACY);
+
+            boolean hasOnlyIssueOnlySources = true;
+            int safetySourcesSize = safetySources.size();
+            for (int i = 0; i < safetySourcesSize; i++) {
+                int type = safetySources.get(i).getType();
+                if (type != SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY) {
+                    hasOnlyIssueOnlySources = false;
+                    break;
+                }
+            }
+
+            int inferredGroupType = SAFETY_SOURCES_GROUP_TYPE_STATELESS;
+            if (hasOnlyIssueOnlySources) {
+                inferredGroupType = SAFETY_SOURCES_GROUP_TYPE_HIDDEN;
+            } else if (summaryResId != Resources.ID_NULL
+                    || statelessIconType != Resources.ID_NULL) {
+                inferredGroupType = SAFETY_SOURCES_GROUP_TYPE_STATEFUL;
+            }
+            int type =
+                    BuilderUtils.validateIntDef(
+                            mType,
+                            "type",
+                            false,
+                            false,
+                            inferredGroupType,
+                            SAFETY_SOURCES_GROUP_TYPE_STATEFUL,
+                            SAFETY_SOURCES_GROUP_TYPE_STATELESS,
+                            SAFETY_SOURCES_GROUP_TYPE_HIDDEN);
+            if (type == SAFETY_SOURCES_GROUP_TYPE_HIDDEN && !hasOnlyIssueOnlySources) {
+                throw new IllegalStateException(
+                        "Safety sources groups of type hidden can only contain sources of type "
+                                + "issue-only");
+            }
+            if (type != SAFETY_SOURCES_GROUP_TYPE_HIDDEN && hasOnlyIssueOnlySources) {
+                throw new IllegalStateException(
+                        "Safety sources groups containing only sources of type issue-only must be "
+                                + "of type hidden");
+            }
+
+            boolean isStateful = type == SAFETY_SOURCES_GROUP_TYPE_STATEFUL;
+            boolean isStateless = type == SAFETY_SOURCES_GROUP_TYPE_STATELESS;
+            int titleResId =
+                    BuilderUtils.validateResId(
+                            mTitleResId, "title", isStateful || isStateless, false);
+
             return new SafetySourcesGroup(
-                    mId, titleResId, summaryResId, statelessIconType, safetySources);
+                    type, id, titleResId, summaryResId, statelessIconType, safetySources);
         }
     }
 }
diff --git a/framework-s/java/android/safetycenter/config/safety_center_config-v34.xsd b/framework-s/java/android/safetycenter/config/safety_center_config-v34.xsd
new file mode 100644
index 0000000..0149779
--- /dev/null
+++ b/framework-s/java/android/safetycenter/config/safety_center_config-v34.xsd
@@ -0,0 +1,217 @@
+<?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 certficate 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"/>
+        <!-- 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 certficate 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"/>
+        <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/framework-s/java/android/safetycenter/config/safety_center_config.xsd b/framework-s/java/android/safetycenter/config/safety_center_config.xsd
index 88cfcfe..283b212 100644
--- a/framework-s/java/android/safetycenter/config/safety_center_config.xsd
+++ b/framework-s/java/android/safetycenter/config/safety_center_config.xsd
@@ -41,7 +41,7 @@
             <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="stringOrStringResourceName" use="required"/>
+        <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"/>
@@ -51,7 +51,7 @@
 
     <xsd:complexType name="dynamic-safety-source">
         <!-- id must be unique among safety sources -->
-        <xsd:attribute name="id" type="stringOrStringResourceName" use="required"/>
+        <xsd:attribute name="id" type="idOrStringResourceName" use="required"/>
         <xsd:attribute name="packageName" type="stringOrStringResourceName" use="required"/>
         <!-- title is required if initialDisplayState is not set to hidden or if searchTerms are provided -->
         <xsd:attribute name="title" type="runtimeStringResourceName"/>
@@ -74,7 +74,7 @@
 
     <xsd:complexType name="issue-only-safety-source">
         <!-- id must be unique among safety sources -->
-        <xsd:attribute name="id" type="stringOrStringResourceName" use="required"/>
+        <xsd:attribute name="id" type="idOrStringResourceName" use="required"/>
         <xsd:attribute name="packageName" type="stringOrStringResourceName" use="required"/>
         <xsd:attribute name="profile" type="profileOrStringResourceName" use="required"/>
         <xsd:attribute name="maxSeverityLevel" type="intOrStringResourceName" default="2147483647"/>
@@ -85,7 +85,7 @@
 
     <xsd:complexType name="static-safety-source">
         <!-- id must be unique among safety sources -->
-        <xsd:attribute name="id" type="stringOrStringResourceName" use="required"/>
+        <xsd:attribute name="id" type="idOrStringResourceName" use="required"/>
         <xsd:attribute name="title" type="runtimeStringResourceName" use="required"/>
         <!-- titleForWork is required if profile is set to all_profiles -->
         <!-- titleForWork is prohibited if profile is set to primary_profile_only -->
@@ -117,6 +117,19 @@
         <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. -->
diff --git a/framework-s/java/android/safetylabel/SafetyLabelConstants.java b/framework-s/java/android/safetylabel/SafetyLabelConstants.java
new file mode 100644
index 0000000..36bde68
--- /dev/null
+++ b/framework-s/java/android/safetylabel/SafetyLabelConstants.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.safetylabel;
+
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+
+import android.annotation.SystemApi;
+
+import androidx.annotation.RequiresApi;
+
+/**
+ * Constants relating to the Safety Label feature.
+ *
+ * @hide
+ */
+@SystemApi
+@RequiresApi(UPSIDE_DOWN_CAKE)
+public final class SafetyLabelConstants {
+    /**
+     * Constant to be used as Device Config flag determining whether the Safety Label Change
+     * Notifications feature is enabled.
+     *
+     * When this flag is enabled, a system notification will be sent to users if any apps they have
+     * installed have made recent updates to their data sharing policy in their app safety labels.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    public static final String SAFETY_LABEL_CHANGE_NOTIFICATION_ENABLED =
+            "safety_label_change_notifications_enabled";
+
+    private SafetyLabelConstants() {}
+}
diff --git a/permissions/com.android.permissioncontroller.xml b/permissions/com.android.permissioncontroller.xml
index 84fe69f..957a29e 100644
--- a/permissions/com.android.permissioncontroller.xml
+++ b/permissions/com.android.permissioncontroller.xml
@@ -33,5 +33,6 @@
         <permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
         <permission name="android.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS" />
         <permission name="android.permission.START_TASKS_FROM_RECENTS" />
+        <permission name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK" />
     </privapp-permissions>
 </permissions>
diff --git a/service/Android.bp b/service/Android.bp
index 39e87a8..4fa96f2 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -71,7 +71,7 @@
     impl_library_visibility: [
         "//frameworks/base/apex/permission/tests",
         "//frameworks/base/services/tests/mockingservicestests",
-        "//frameworks/base/services/tests/servicestests",
+        "//frameworks/base/services/tests/PackageManagerServiceTests/server",
         "//packages/modules/Permission/tests/apex",
     ],
     srcs: [
@@ -79,6 +79,7 @@
     ],
     libs: [
         "androidx.annotation_annotation",
+        "framework-configinfrastructure",
         // TODO(b/177884622): Short term solution to prevent service-permission from seeing hidden
         //  APIs in framework-permission, as we don't actually have any dependency in it.
         //"framework-permission",
@@ -94,6 +95,7 @@
     static_libs: [
         "kotlin-stdlib",
         "modules-utils-backgroundthread",
+        "modules-utils-binary-xml",
         "modules-utils-os",
         "safety-center-config",
         "safety-center-internal-data",
diff --git a/service/java/com/android/permission/access/AccessCheckingService.kt b/service/java/com/android/permission/access/AccessCheckingService.kt
deleted file mode 100644
index 7187526..0000000
--- a/service/java/com/android/permission/access/AccessCheckingService.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access
-
-import androidx.annotation.Keep
-import com.android.permission.access.external.PackageState
-
-@Keep
-class AccessCheckingService {
-    @Volatile
-    private lateinit var state: AccessState
-    private val stateLock = Any()
-
-    private val policy = AccessPolicy()
-
-    private val persistence = AccessPersistence()
-
-    fun init() {
-        val state = AccessState()
-        state.systemState.userIds.apply {
-            // TODO: Get and add all user IDs.
-            // TODO: Maybe get and add all packages?
-        }
-        persistence.read(state)
-        this.state = state
-    }
-
-    fun getDecision(subject: AccessUri, `object`: AccessUri): Int =
-        policy.getDecision(subject, `object`, state)
-
-    fun setDecision(subject: AccessUri, `object`: AccessUri, decision: Int) {
-        mutateState { oldState, newState ->
-            policy.setDecision(subject, `object`, decision, oldState, newState)
-        }
-    }
-
-    fun onUserAdded(userId: Int) {
-        mutateState { oldState, newState ->
-            policy.onUserAdded(userId, oldState, newState)
-        }
-    }
-
-    fun onUserRemoved(userId: Int) {
-        mutateState { oldState, newState ->
-            policy.onUserRemoved(userId, oldState, newState)
-        }
-    }
-
-    fun onPackageAdded(packageState: PackageState) {
-        mutateState { oldState, newState ->
-            policy.onPackageAdded(packageState, oldState, newState)
-        }
-    }
-
-    fun onPackageRemoved(packageState: PackageState) {
-        mutateState { oldState, newState ->
-            policy.onPackageRemoved(packageState, oldState, newState)
-        }
-    }
-
-    // TODO: Replace (oldState, newState) with Kotlin context receiver once it's stabilized.
-    private inline fun mutateState(action: (oldState: AccessState, newState: AccessState) -> Unit) {
-        synchronized(stateLock) {
-            val oldState = state
-            val newState = oldState.copy()
-            action(oldState, newState)
-            persistence.write(newState)
-            state = newState
-        }
-    }
-}
diff --git a/service/java/com/android/permission/access/AccessDecisions.kt b/service/java/com/android/permission/access/AccessDecisions.kt
deleted file mode 100644
index 70dd348..0000000
--- a/service/java/com/android/permission/access/AccessDecisions.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access
-
-object AccessDecisions {
-    const val DENIED = 0
-}
diff --git a/service/java/com/android/permission/access/AccessPersistence.kt b/service/java/com/android/permission/access/AccessPersistence.kt
deleted file mode 100644
index 4c2c09d..0000000
--- a/service/java/com/android/permission/access/AccessPersistence.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access
-
-import com.android.permission.access.util.* // ktlint-disable no-wildcard-imports
-
-class AccessPersistence {
-    fun read(state: AccessState) {
-        readSystemState(state.systemState)
-        val userStates = state.userStates
-        state.systemState.userIds.forEachIndexed { _, userId ->
-            readUserState(userId, userStates.getOrPut(userId) { UserState() })
-        }
-    }
-
-    private fun readSystemState(systemState: SystemState) {
-        TODO()
-    }
-
-    private fun readUserState(userId: Int, userState: UserState) {
-        TODO()
-    }
-
-    fun write(state: AccessState) {
-        writeState(state.systemState, ::writeSystemState)
-        state.userStates.forEachIndexed { _, userId, userState ->
-            writeState(userState) { writeUserState(userId, it) }
-        }
-    }
-
-    private inline fun <T : WritableState> writeState(state: T, write: (T) -> Unit) {
-        when (val writeMode = state.writeMode) {
-            WriteMode.NONE -> {}
-            WriteMode.SYNC -> write(state)
-            WriteMode.ASYNC -> TODO()
-            else -> error(writeMode)
-        }
-    }
-
-    private fun writeSystemState(systemState: SystemState) {
-        TODO()
-    }
-
-    private fun writeUserState(userId: Int, userState: UserState) {
-        TODO()
-    }
-}
diff --git a/service/java/com/android/permission/access/AccessPolicy.kt b/service/java/com/android/permission/access/AccessPolicy.kt
deleted file mode 100644
index 9084d5b..0000000
--- a/service/java/com/android/permission/access/AccessPolicy.kt
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access
-
-import com.android.permission.access.external.PackageState
-import com.android.permission.access.permission.UidPermissionPolicy
-import com.android.permission.access.util.* // ktlint-disable no-wildcard-imports
-
-class AccessPolicy private constructor(
-    private val schemePolicies: IndexedMap<String, IndexedMap<String, SchemePolicy>>
-) {
-    constructor() : this(
-        IndexedMap<String, IndexedMap<String, SchemePolicy>>().apply {
-            val uidPermissionPolicy = UidPermissionPolicy()
-            getOrPut(uidPermissionPolicy.subjectScheme) { IndexedMap() }
-                .put(uidPermissionPolicy.objectScheme, uidPermissionPolicy)
-        }
-    )
-
-    fun getDecision(subject: AccessUri, `object`: AccessUri, state: AccessState): Int {
-        // TODO: Warn when not found?
-        val schemePolicy = getSchemePolicy(subject, `object`) ?: return AccessDecisions.DENIED
-        return schemePolicy.getDecision(subject, `object`, state)
-    }
-
-    fun setDecision(
-        subject: AccessUri,
-        `object`: AccessUri,
-        decision: Int,
-        oldState: AccessState,
-        newState: AccessState
-    ) {
-        // TODO: Warn when not found?
-        val schemePolicy = getSchemePolicy(subject, `object`) ?: return
-        return schemePolicy.setDecision(subject, `object`, decision, oldState, newState)
-    }
-
-    private fun getSchemePolicy(subject: AccessUri, `object`: AccessUri): SchemePolicy? =
-        schemePolicies[subject.scheme]?.get(`object`.scheme)
-
-    fun onUserAdded(userId: Int, oldState: AccessState, newState: AccessState) {
-        newState.systemState.userIds += userId
-        newState.userStates[userId] = UserState()
-        forEachSchemePolicy { it.onUserAdded(userId, oldState, newState) }
-    }
-
-    fun onUserRemoved(userId: Int, oldState: AccessState, newState: AccessState) {
-        newState.systemState.userIds -= userId
-        newState.userStates -= userId
-        forEachSchemePolicy { it.onUserRemoved(userId, oldState, newState) }
-    }
-
-    fun onPackageAdded(packageState: PackageState, oldState: AccessState, newState: AccessState) {
-        var isAppIdAdded = false
-        newState.systemState.apply {
-            packageStates[packageState.packageName] = packageState
-            appIds.getOrPut(packageState.appId) {
-                isAppIdAdded = true
-                IndexedListSet()
-            }.add(packageState.packageName)
-        }
-        if (isAppIdAdded) {
-            forEachSchemePolicy { it.onAppIdAdded(packageState.appId, oldState, newState) }
-        }
-        forEachSchemePolicy { it.onPackageAdded(packageState, oldState, newState) }
-    }
-
-    fun onPackageRemoved(packageState: PackageState, oldState: AccessState, newState: AccessState) {
-        var isAppIdRemoved = false
-        newState.systemState.apply {
-            packageStates -= packageState.packageName
-            appIds.apply appIds@{
-                this[packageState.appId]?.apply {
-                    this -= packageState.packageName
-                    if (isEmpty()) {
-                        this@appIds -= packageState.appId
-                        isAppIdRemoved = true
-                    }
-                }
-            }
-        }
-        forEachSchemePolicy { it.onPackageRemoved(packageState, oldState, newState) }
-        if (isAppIdRemoved) {
-            forEachSchemePolicy { it.onAppIdRemoved(packageState.appId, oldState, newState) }
-        }
-    }
-
-    private inline fun forEachSchemePolicy(action: (SchemePolicy) -> Unit) {
-        schemePolicies.forEachValueIndexed { _, it ->
-            it.forEachValueIndexed { _, it ->
-                action(it)
-            }
-        }
-    }
-}
-
-abstract class SchemePolicy {
-    abstract val subjectScheme: String
-
-    abstract val objectScheme: String
-
-    abstract fun getDecision(subject: AccessUri, `object`: AccessUri, state: AccessState): Int
-
-    abstract fun setDecision(
-        subject: AccessUri,
-        `object`: AccessUri,
-        decision: Int,
-        oldState: AccessState,
-        newState: AccessState
-    )
-
-    abstract fun onUserAdded(userId: Int, oldState: AccessState, newState: AccessState)
-
-    abstract fun onUserRemoved(userId: Int, oldState: AccessState, newState: AccessState)
-
-    abstract fun onAppIdAdded(appId: Int, oldState: AccessState, newState: AccessState)
-
-    abstract fun onAppIdRemoved(appId: Int, oldState: AccessState, newState: AccessState)
-
-    abstract fun onPackageAdded(
-        packageState: PackageState,
-        oldState: AccessState,
-        newState: AccessState
-    )
-
-    abstract fun onPackageRemoved(
-        packageState: PackageState,
-        oldState: AccessState,
-        newState: AccessState
-    )
-}
diff --git a/service/java/com/android/permission/access/AccessState.kt b/service/java/com/android/permission/access/AccessState.kt
deleted file mode 100644
index 00bc0d6..0000000
--- a/service/java/com/android/permission/access/AccessState.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access
-
-import android.content.pm.PermissionGroupInfo
-import com.android.permission.access.data.Permission
-import com.android.permission.access.external.PackageState
-import com.android.permission.access.util.* // ktlint-disable no-wildcard-imports
-
-class AccessState private constructor(
-    val systemState: SystemState,
-    val userStates: IntMap<UserState>
-) {
-    constructor() : this(SystemState(), IntMap())
-
-    fun copy(): AccessState = AccessState(systemState.copy(), userStates.copy { it.copy() })
-}
-
-class SystemState private constructor(
-    val userIds: IntSet,
-    val packageStates: IndexedMap<String, PackageState>,
-    val appIds: IntMap<IndexedListSet<String>>,
-    val permissionGroups: IndexedMap<String, PermissionGroupInfo>,
-    val permissionTrees: IndexedMap<String, Permission>,
-    val permissions: IndexedMap<String, Permission>
-) : WritableState() {
-    constructor() : this(IntSet(), IndexedMap(), IntMap(), IndexedMap(), IndexedMap(), IndexedMap())
-
-    fun copy(): SystemState =
-        SystemState(
-            userIds.copy(), packageStates.copy { it }, appIds.copy { it.copy() },
-            permissionGroups.copy { it }, permissionTrees.copy { it }, permissions.copy { it }
-        )
-}
-
-class UserState private constructor(
-    val permissionFlags: IntMap<IndexedMap<String, Int>>
-) : WritableState() {
-    constructor() : this(IntMap())
-
-    fun copy(): UserState = UserState(permissionFlags.copy { it.copy { it } })
-}
-
-object WriteMode {
-    const val NONE = 0
-    const val SYNC = 1
-    const val ASYNC = 2
-}
-
-abstract class WritableState {
-    var writeMode: Int = WriteMode.NONE
-        private set
-
-    fun requestWrite(sync: Boolean = false) {
-        if (sync) {
-            writeMode = WriteMode.SYNC
-        } else {
-            if (writeMode != WriteMode.SYNC) {
-                writeMode = WriteMode.ASYNC
-            }
-        }
-    }
-}
diff --git a/service/java/com/android/permission/access/AccessUri.kt b/service/java/com/android/permission/access/AccessUri.kt
deleted file mode 100644
index e7a05ba..0000000
--- a/service/java/com/android/permission/access/AccessUri.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access
-
-import com.android.permission.access.external.UserHandle
-import com.android.permission.access.external.UserHandleCompat
-
-sealed class AccessUri(
-    val scheme: String
-) {
-    override fun equals(other: Any?): Boolean {
-        throw NotImplementedError()
-    }
-
-    override fun hashCode(): Int {
-        throw NotImplementedError()
-    }
-
-    override fun toString(): String {
-        throw NotImplementedError()
-    }
-}
-
-data class AppOpUri(
-    val appOpName: String
-) : AccessUri(SCHEME) {
-    override fun toString(): String = "$scheme:///$appOpName"
-
-    companion object {
-        const val SCHEME = "app-op"
-    }
-}
-
-data class PackageUri(
-    val packageName: String,
-    val userId: Int
-) : AccessUri(SCHEME) {
-    override fun toString(): String = "$scheme:///$packageName/$userId"
-
-    companion object {
-        const val SCHEME = "package"
-    }
-}
-
-data class PermissionUri(
-    val permissionName: String
-) : AccessUri(SCHEME) {
-    override fun toString(): String = "$scheme:///$permissionName"
-
-    companion object {
-        const val SCHEME = "permission"
-    }
-}
-
-data class UidUri(
-    val uid: Int
-) : AccessUri(SCHEME) {
-    val userId: Int
-        get() = UserHandleCompat.getUserId(uid)
-
-    val appId: Int
-        get() = UserHandle.getAppId(uid)
-
-    override fun toString(): String = "$scheme:///$uid"
-
-    companion object {
-        const val SCHEME = "uid"
-    }
-}
diff --git a/service/java/com/android/permission/access/data/Package.kt b/service/java/com/android/permission/access/data/Package.kt
deleted file mode 100644
index 6c34055..0000000
--- a/service/java/com/android/permission/access/data/Package.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access.data
-
-import com.android.permission.access.external.AndroidPackage
-
-class Package(
-    private val androidPackage: AndroidPackage
-) {
-    val name: String
-        get() = androidPackage.packageName
-
-    val adoptPermissions: List<String>
-        get() = androidPackage.adoptPermissions
-
-    val appId: Int
-        get() = androidPackage.appId
-
-    val requestedPermissions: List<String>
-        get() = androidPackage.requestedPermissions
-
-    override fun equals(other: Any?): Boolean {
-        throw NotImplementedError()
-    }
-
-    override fun hashCode(): Int {
-        throw NotImplementedError()
-    }
-}
diff --git a/service/java/com/android/permission/access/data/Permission.kt b/service/java/com/android/permission/access/data/Permission.kt
deleted file mode 100644
index 8ca268c..0000000
--- a/service/java/com/android/permission/access/data/Permission.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access.data
-
-import android.content.pm.PermissionInfo
-
-class Permission(
-    val permissionInfo: PermissionInfo,
-    val type: Int,
-    val isReconciled: Boolean
-) {
-    val name: String
-        get() = permissionInfo.name
-
-    val packageName: String
-        get() = permissionInfo.packageName
-
-    companion object {
-        // The permission is defined in an application manifest.
-        const val TYPE_MANIFEST = 0
-        // The permission is defined in a system config.
-        const val TYPE_CONFIG = 1
-        // The permission is defined dynamically.
-        const val TYPE_DYNAMIC = 2
-    }
-}
diff --git a/service/java/com/android/permission/access/external/PackageInfoUtils.kt b/service/java/com/android/permission/access/external/PackageInfoUtils.kt
deleted file mode 100644
index 1ee4bfa..0000000
--- a/service/java/com/android/permission/access/external/PackageInfoUtils.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access.external
-
-import android.content.pm.PermissionGroupInfo
-import android.content.pm.PermissionInfo
-
-object PackageInfoUtils {
-    fun generatePermissionInfo(parsedPermission: ParsedPermission, flags: Long): PermissionInfo {
-        throw NotImplementedError()
-    }
-
-    fun generatePermissionGroupInfo(
-        parsedPermissionGroup: ParsedPermissionGroup,
-        flags: Long
-    ): PermissionGroupInfo {
-        throw NotImplementedError()
-    }
-}
diff --git a/service/java/com/android/permission/access/external/PackageState.kt b/service/java/com/android/permission/access/external/PackageState.kt
deleted file mode 100644
index c88ef16..0000000
--- a/service/java/com/android/permission/access/external/PackageState.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access.external
-
-import android.util.SparseArray
-
-interface PackageState {
-    val androidPackage: AndroidPackage?
-    val appId: Int
-    val isSystem: Boolean
-    val packageName: String
-    val userStates: SparseArray<PackageUserState>
-}
-
-interface AndroidPackage {
-    val packageName: String
-    val appId: Int
-    val adoptPermissions: List<String>
-    val permissions: List<ParsedPermission>
-    val permissionGroups: List<ParsedPermissionGroup>
-    val requestedPermissions: List<String>
-}
-
-interface ParsedPermission {
-    val name: String
-    val isTree: Boolean
-    val packageName: String
-}
-
-interface ParsedPermissionGroup {
-    val name: String
-    val packageName: String
-}
-
-interface PackageUserState {
-    val isInstantApp: Boolean
-}
diff --git a/service/java/com/android/permission/access/external/UserHandle.kt b/service/java/com/android/permission/access/external/UserHandle.kt
deleted file mode 100644
index 2c30dd9..0000000
--- a/service/java/com/android/permission/access/external/UserHandle.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access.external
-
-interface UserHandle {
-    companion object {
-        fun getAppId(uid: Int): Int {
-            throw NotImplementedError()
-        }
-    }
-}
-
-object UserHandleCompat {
-    fun getUserId(uid: Int): Int {
-        throw NotImplementedError()
-    }
-}
diff --git a/service/java/com/android/permission/access/permission/UidPermissionPolicy.kt b/service/java/com/android/permission/access/permission/UidPermissionPolicy.kt
deleted file mode 100644
index fb2c2fd..0000000
--- a/service/java/com/android/permission/access/permission/UidPermissionPolicy.kt
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access.permission
-
-import android.content.pm.PackageManager
-import android.content.pm.PermissionInfo
-import android.util.Log
-import com.android.permission.access.AccessDecisions
-import com.android.permission.access.AccessState
-import com.android.permission.access.AccessUri
-import com.android.permission.access.PermissionUri
-import com.android.permission.access.SchemePolicy
-import com.android.permission.access.UidUri
-import com.android.permission.access.UserState
-import com.android.permission.access.data.Permission
-import com.android.permission.access.external.PackageInfoUtils
-import com.android.permission.access.external.PackageState
-import com.android.permission.access.util.* // ktlint-disable no-wildcard-imports
-
-private val LOG_TAG = UidPermissionPolicy::class.java.simpleName
-
-class UidPermissionPolicy : SchemePolicy() {
-    override val subjectScheme: String
-        get() = UidUri.SCHEME
-
-    override val objectScheme: String
-        get() = PermissionUri.SCHEME
-
-    override fun getDecision(subject: AccessUri, `object`: AccessUri, state: AccessState): Int {
-        subject as UidUri
-        `object` as PermissionUri
-        val flags = state.userStates[subject.userId]?.permissionFlags?.get(subject.appId)
-            ?.get(`object`.permissionName) ?: return AccessDecisions.DENIED
-        return when (flags) {
-            // TODO
-            0 -> AccessDecisions.DENIED
-            else -> error(flags)
-        }
-    }
-
-    override fun setDecision(
-        subject: AccessUri,
-        `object`: AccessUri,
-        decision: Int,
-        oldState: AccessState,
-        newState: AccessState
-    ) {
-        subject as UidUri
-        `object` as PermissionUri
-        val uidFlags = newState.userStates.getOrPut(subject.userId) { UserState() }
-            .permissionFlags.getOrPut(subject.appId) { IndexedMap() }
-        val flags = when (decision) {
-            // TODO
-            AccessDecisions.DENIED -> 0
-            else -> error(decision)
-        }
-        uidFlags[`object`.permissionName] = flags
-    }
-
-    override fun onUserAdded(userId: Int, oldState: AccessState, newState: AccessState) {
-        // NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG
-        //updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, true)
-    }
-
-    override fun onUserRemoved(userId: Int, oldState: AccessState, newState: AccessState) {}
-
-    override fun onAppIdAdded(appId: Int, oldState: AccessState, newState: AccessState) {
-        // TODO
-    }
-
-    override fun onAppIdRemoved(appId: Int, oldState: AccessState, newState: AccessState) {
-        // TODO
-    }
-
-    override fun onPackageAdded(
-        packageState: PackageState,
-        oldState: AccessState,
-        newState: AccessState
-    ) {
-        adoptPermissions(packageState, newState)
-        addPermissionGroups(packageState, newState)
-        // TODO: addPermissions(packageState, newState)
-        // TODO: revokeStoragePermissionsIfScopeExpandedInternal()
-        // TODO: Changed: updatePermissions() equivalent here to create permission state
-        //  immediately.
-    }
-
-    private fun adoptPermissions(packageState: PackageState, newState: AccessState) {
-        val `package` = packageState.androidPackage!!
-        `package`.adoptPermissions.forEachIndexed { _, originalPackageName ->
-            val packageName = `package`.packageName
-            if (!canAdoptPermissions(packageName, originalPackageName, newState)) {
-                return@forEachIndexed
-            }
-            newState.systemState.permissions.let { permissions ->
-                permissions.forEachIndexed { i, permissionName, oldPermission ->
-                    if (oldPermission.packageName != originalPackageName) {
-                        return@forEachIndexed
-                    }
-                    @Suppress("DEPRECATION")
-                    val newPermissionInfo = PermissionInfo().apply {
-                        name = oldPermission.permissionInfo.name
-                        this.packageName = packageName
-                        protectionLevel = oldPermission.permissionInfo.protectionLevel
-                    }
-                    val newPermission = Permission(newPermissionInfo, oldPermission.type, false)
-                    permissions.setValueAt(i, newPermission)
-                }
-            }
-        }
-    }
-
-    private fun canAdoptPermissions(
-        packageName: String,
-        originalPackageName: String,
-        newState: AccessState
-    ): Boolean {
-        val originalPackageState = newState.systemState.packageStates[originalPackageName]
-            ?: return false
-        if (!originalPackageState.isSystem) {
-            Log.w(
-                LOG_TAG, "Unable to adopt permissions from $originalPackageName to $packageName:" +
-                    " original package not in system partition"
-            )
-            return false
-        }
-        if (originalPackageState.androidPackage != null) {
-            Log.w(
-                LOG_TAG, "Unable to adopt permissions from $originalPackageName to $packageName:" +
-                    " original package still exists"
-            )
-            return false
-        }
-        return true
-    }
-
-    private fun addPermissionGroups(packageState: PackageState, newState: AccessState) {
-        // Different from the old implementation, which decides whether the app is an instant app by
-        // the install flags, now for consistent behavior we allow adding permission groups if the
-        // app is non-instant in at least one user.
-        val isInstantApp = packageState.userStates.allIndexed { _, _, it -> it.isInstantApp }
-        if (isInstantApp) {
-            Log.w(
-                LOG_TAG, "Ignoring permission groups declared in package" +
-                    " ${packageState.packageName}: instant apps cannot declare permission groups"
-            )
-            return
-        }
-        packageState.androidPackage!!.permissionGroups.forEachIndexed { _, parsedPermissionGroup ->
-            val newPermissionGroup = PackageInfoUtils.generatePermissionGroupInfo(
-                parsedPermissionGroup, PackageManager.GET_META_DATA.toLong()
-            )
-            // TODO: Clear permission state on group take-over?
-            val permissionGroupName = newPermissionGroup.name
-            val oldPermissionGroup = newState.systemState.permissionGroups[permissionGroupName]
-            if (oldPermissionGroup != null &&
-                newPermissionGroup.packageName != oldPermissionGroup.packageName) {
-                Log.w(
-                    LOG_TAG, "Ignoring permission group $permissionGroupName declared in package" +
-                        " ${newPermissionGroup.packageName}: already declared in another package" +
-                        " ${oldPermissionGroup.packageName}"
-                )
-                return@forEachIndexed
-            }
-            newState.systemState.permissionGroups[permissionGroupName] = newPermissionGroup
-        }
-    }
-
-    override fun onPackageRemoved(
-        packageState: PackageState,
-        oldState: AccessState,
-        newState: AccessState
-    ) {
-        // TODO
-    }
-}
diff --git a/service/java/com/android/permission/access/util/IndexedList.kt b/service/java/com/android/permission/access/util/IndexedList.kt
deleted file mode 100644
index 94c0ac6..0000000
--- a/service/java/com/android/permission/access/util/IndexedList.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access.util
-
-typealias IndexedList<T> = ArrayList<T>
-
-inline fun <T> IndexedList<T>.allIndexed(predicate: (Int, T) -> Boolean): Boolean {
-    for (index in 0 until size) {
-        if (!predicate(index, this[index])) {
-            return false
-        }
-    }
-    return true
-}
-
-inline fun <T> IndexedList<T>.anyIndexed(predicate: (Int, T) -> Boolean): Boolean {
-    for (index in 0 until size) {
-        if (predicate(index, this[index])) {
-            return true
-        }
-    }
-    return false
-}
-
-@Suppress("NOTHING_TO_INLINE")
-inline fun <T> IndexedList<T>.copy(): IndexedList<T> = IndexedList(this)
-
-inline fun <T> IndexedList<T>.forEachIndexed(action: (Int, T) -> Unit) {
-    for (index in indices) {
-        action(index, this[index])
-    }
-}
-
-@Suppress("NOTHING_TO_INLINE")
-inline operator fun <T> IndexedList<T>.minusAssign(element: T) {
-    remove(element)
-}
-
-@Suppress("NOTHING_TO_INLINE")
-inline operator fun <T> IndexedList<T>.plusAssign(element: T) {
-    add(element)
-}
diff --git a/service/java/com/android/permission/access/util/IndexedListSet.kt b/service/java/com/android/permission/access/util/IndexedListSet.kt
deleted file mode 100644
index 4e50e58..0000000
--- a/service/java/com/android/permission/access/util/IndexedListSet.kt
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access.util
-
-class IndexedListSet<T> private constructor(
-    private val list: ArrayList<T>
-) : MutableSet<T> {
-    constructor() : this(ArrayList())
-
-    override val size: Int
-        get() = list.size
-
-    override fun contains(element: T): Boolean = list.contains(element)
-
-    override fun isEmpty(): Boolean = list.isEmpty()
-
-    override fun iterator(): MutableIterator<T> = list.iterator()
-
-    override fun containsAll(elements: Collection<T>): Boolean {
-        throw NotImplementedError()
-    }
-
-    fun elementAt(index: Int): T = list[index]
-
-    fun indexOf(element: T): Int = list.indexOf(element)
-
-    override fun add(element: T): Boolean =
-        if (list.contains(element)) {
-            false
-        } else {
-            list.add(element)
-            true
-        }
-
-    override fun remove(element: T): Boolean = list.remove(element)
-
-    override fun clear() {
-        list.clear()
-    }
-
-    override fun addAll(elements: Collection<T>): Boolean {
-        throw NotImplementedError()
-    }
-
-    override fun removeAll(elements: Collection<T>): Boolean {
-        throw NotImplementedError()
-    }
-
-    override fun retainAll(elements: Collection<T>): Boolean {
-        throw NotImplementedError()
-    }
-
-    fun removeAt(index: Int): T? = list.removeAt(index)
-
-    fun copy(): IndexedListSet<T> = IndexedListSet(ArrayList(list))
-}
-
-inline fun <T> IndexedListSet<T>.allIndexed(predicate: (Int, T) -> Boolean): Boolean {
-    for (index in 0 until size) {
-        if (!predicate(index, elementAt(index))) {
-            return false
-        }
-    }
-    return true
-}
-
-inline fun <T> IndexedListSet<T>.anyIndexed(predicate: (Int, T) -> Boolean): Boolean {
-    for (index in 0 until size) {
-        if (predicate(index, elementAt(index))) {
-            return true
-        }
-    }
-    return false
-}
-
-inline fun <T> IndexedListSet<T>.forEachIndexed(action: (Int, T) -> Unit) {
-    for (index in indices) {
-        action(index, elementAt(index))
-    }
-}
-
-@Suppress("NOTHING_TO_INLINE")
-inline operator fun <T> IndexedListSet<T>.minusAssign(element: T) {
-    remove(element)
-}
-
-@Suppress("NOTHING_TO_INLINE")
-inline operator fun <T> IndexedListSet<T>.plusAssign(element: T) {
-    add(element)
-}
diff --git a/service/java/com/android/permission/access/util/IndexedMap.kt b/service/java/com/android/permission/access/util/IndexedMap.kt
deleted file mode 100644
index 33843b1..0000000
--- a/service/java/com/android/permission/access/util/IndexedMap.kt
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access.util
-
-import android.util.ArrayMap
-
-typealias IndexedMap<K, V> = ArrayMap<K, V>
-
-inline fun <K, V> IndexedMap<K, V>.allIndexed(predicate: (Int, K, V) -> Boolean): Boolean {
-    for (index in 0 until size) {
-        if (!predicate(index, keyAt(index), valueAt(index))) {
-            return false
-        }
-    }
-    return true
-}
-
-inline fun <K, V> IndexedMap<K, V>.anyIndexed(predicate: (Int, K, V) -> Boolean): Boolean {
-    for (index in 0 until size) {
-        if (predicate(index, keyAt(index), valueAt(index))) {
-            return true
-        }
-    }
-    return false
-}
-
-inline fun <K, V> IndexedMap<K, V>.copy(copyValue: (V) -> V): IndexedMap<K, V> =
-    IndexedMap(this).apply {
-        forEachValueIndexed { index, value ->
-            setValueAt(index, copyValue(value))
-        }
-    }
-
-inline fun <K, V, R> IndexedMap<K, V>.firstNotNullOfOrNullIndexed(transform: (Int, K, V) -> R): R? {
-    for (index in 0 until size) {
-        transform(index, keyAt(index), valueAt(index))?.let { return it }
-    }
-    return null
-}
-
-inline fun <K, V> IndexedMap<K, V>.forEachIndexed(action: (Int, K, V) -> Unit) {
-    for (index in 0 until size) {
-        action(index, keyAt(index), valueAt(index))
-    }
-}
-
-inline fun <K, V> IndexedMap<K, V>.forEachKeyIndexed(action: (Int, K) -> Unit) {
-    for (index in 0 until size) {
-        action(index, keyAt(index))
-    }
-}
-
-inline fun <K, V> IndexedMap<K, V>.forEachValueIndexed(action: (Int, V) -> Unit) {
-    for (index in 0 until size) {
-        action(index, valueAt(index))
-    }
-}
-
-inline fun <K, V> IndexedMap<K, V>.getOrPut(key: K, defaultValue: () -> V): V {
-    get(key)?.let { return it }
-    return defaultValue().also { put(key, it) }
-}
-
-@Suppress("NOTHING_TO_INLINE")
-inline operator fun <K, V> IndexedMap<K, V>.minusAssign(key: K) {
-    remove(key)
-}
-
-@Suppress("NOTHING_TO_INLINE")
-inline operator fun <K, V> IndexedMap<K, V>.set(key: K, value: V) {
-    put(key, value)
-}
diff --git a/service/java/com/android/permission/access/util/IndexedSet.kt b/service/java/com/android/permission/access/util/IndexedSet.kt
deleted file mode 100644
index 17e413c..0000000
--- a/service/java/com/android/permission/access/util/IndexedSet.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access.util
-
-import android.util.ArraySet
-
-typealias IndexedSet<T> = ArraySet<T>
-
-inline fun <T> IndexedSet<T>.allIndexed(predicate: (Int, T) -> Boolean): Boolean {
-    for (index in 0 until size) {
-        if (!predicate(index, elementAt(index))) {
-            return false
-        }
-    }
-    return true
-}
-
-inline fun <T> IndexedSet<T>.anyIndexed(predicate: (Int, T) -> Boolean): Boolean {
-    for (index in 0 until size) {
-        if (predicate(index, elementAt(index))) {
-            return true
-        }
-    }
-    return false
-}
-
-@Suppress("NOTHING_TO_INLINE")
-inline fun <T> IndexedSet<T>.copy(): IndexedSet<T> = IndexedSet(this)
-
-@Suppress("NOTHING_TO_INLINE")
-inline fun <T> IndexedSet<T>.elementAt(index: Int): T = valueAt(index)
-
-inline fun <T> IndexedSet<T>.forEachIndexed(action: (Int, T) -> Unit) {
-    for (index in indices) {
-        action(index, elementAt(index))
-    }
-}
-
-@Suppress("NOTHING_TO_INLINE")
-inline operator fun <T> IndexedSet<T>.minusAssign(element: T) {
-    remove(element)
-}
-
-@Suppress("NOTHING_TO_INLINE")
-inline operator fun <T> IndexedSet<T>.plusAssign(element: T) {
-    add(element)
-}
diff --git a/service/java/com/android/permission/access/util/IntMap.kt b/service/java/com/android/permission/access/util/IntMap.kt
deleted file mode 100644
index bdff43d..0000000
--- a/service/java/com/android/permission/access/util/IntMap.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access.util
-
-import android.util.SparseArray
-
-typealias IntMap<T> = SparseArray<T>
-
-inline fun <T> IntMap<T>.allIndexed(predicate: (Int, Int, T) -> Boolean): Boolean {
-    for (index in 0 until size) {
-        if (!predicate(index, keyAt(index), valueAt(index))) {
-            return false
-        }
-    }
-    return true
-}
-
-inline fun <T> IntMap<T>.anyIndexed(predicate: (Int, Int, T) -> Boolean): Boolean {
-    for (index in 0 until size) {
-        if (predicate(index, keyAt(index), valueAt(index))) {
-            return true
-        }
-    }
-    return false
-}
-
-inline fun <T> IntMap<T>.copy(copyValue: (T) -> T): IntMap<T> =
-    this.clone().apply {
-        forEachValueIndexed { index, value ->
-            setValueAt(index, copyValue(value))
-        }
-    }
-
-inline fun <T> IntMap<T>.forEachIndexed(action: (Int, Int, T) -> Unit) {
-    for (index in 0 until size) {
-        action(index, keyAt(index), valueAt(index))
-    }
-}
-
-inline fun <T> IntMap<T>.forEachKeyIndexed(action: (Int, Int) -> Unit) {
-    for (index in 0 until size) {
-        action(index, keyAt(index))
-    }
-}
-
-inline fun <T> IntMap<T>.forEachValueIndexed(action: (Int, T) -> Unit) {
-    for (index in 0 until size) {
-        action(index, valueAt(index))
-    }
-}
-
-inline fun <T> IntMap<T>.getOrPut(key: Int, defaultValue: () -> T): T {
-    get(key)?.let { return it }
-    return defaultValue().also { put(key, it) }
-}
-
-@Suppress("NOTHING_TO_INLINE")
-inline operator fun <T> IntMap<T>.minusAssign(key: Int) {
-    remove(key)
-}
-
-inline val <T> IntMap<T>.size: Int
-    get() = size()
diff --git a/service/java/com/android/permission/access/util/IntSet.kt b/service/java/com/android/permission/access/util/IntSet.kt
deleted file mode 100644
index 81eb294..0000000
--- a/service/java/com/android/permission/access/util/IntSet.kt
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.permission.access.util
-
-import android.util.SparseBooleanArray
-
-class IntSet private constructor(
-    private val array: SparseBooleanArray
-) {
-    constructor() : this(SparseBooleanArray())
-
-    val size: Int
-        get() = array.size()
-
-    operator fun contains(element: Int): Boolean = array[element]
-
-    fun elementAt(index: Int): Int = array.keyAt(index)
-
-    fun indexOf(element: Int): Int = array.indexOfKey(element)
-
-    fun add(element: Int) {
-        array.put(element, true)
-    }
-
-    fun remove(element: Int) {
-        array.delete(element)
-    }
-
-    fun clear() {
-        array.clear()
-    }
-
-    fun removeAt(index: Int) {
-        array.removeAt(index)
-    }
-
-    fun copy(): IntSet = IntSet(array.clone())
-}
-
-inline fun IntSet.allIndexed(predicate: (Int, Int) -> Boolean): Boolean {
-    for (index in 0 until size) {
-        if (!predicate(index, elementAt(index))) {
-            return false
-        }
-    }
-    return true
-}
-
-inline fun IntSet.anyIndexed(predicate: (Int, Int) -> Boolean): Boolean {
-    for (index in 0 until size) {
-        if (predicate(index, elementAt(index))) {
-            return true
-        }
-    }
-    return false
-}
-
-inline fun IntSet.forEachIndexed(action: (Int, Int) -> Unit) {
-    for (index in 0 until size) {
-        action(index, elementAt(index))
-    }
-}
-
-@Suppress("NOTHING_TO_INLINE")
-inline operator fun IntSet.minusAssign(element: Int) {
-    remove(element)
-}
-
-@Suppress("NOTHING_TO_INLINE")
-inline operator fun IntSet.plusAssign(element: Int) {
-    add(element)
-}
diff --git a/service/java/com/android/permission/compat/UserHandleCompat.java b/service/java/com/android/permission/compat/UserHandleCompat.java
index 7c711d3..1901aa9 100644
--- a/service/java/com/android/permission/compat/UserHandleCompat.java
+++ b/service/java/com/android/permission/compat/UserHandleCompat.java
@@ -45,4 +45,15 @@
     public static int getUserId(int uid) {
         return UserHandle.getUserHandleForUid(uid).getIdentifier();
     }
+
+    /**
+     * Get the UID from the give user ID and app ID
+     *
+     * @param userId the user ID
+     * @param appId the app ID
+     * @return the UID
+     */
+    public static int getUid(@UserIdInt int userId, int appId) {
+        return UserHandle.of(userId).getUid(appId);
+    }
 }
diff --git a/service/java/com/android/safetycenter/PendingIntentFactory.java b/service/java/com/android/safetycenter/PendingIntentFactory.java
index e54b887..42dcbca 100644
--- a/service/java/com/android/safetycenter/PendingIntentFactory.java
+++ b/service/java/com/android/safetycenter/PendingIntentFactory.java
@@ -26,28 +26,44 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.ResolveInfoFlags;
 import android.content.res.Resources;
 import android.os.Binder;
 import android.os.UserHandle;
+import android.safetycenter.SafetyCenterEntry;
 import android.util.Log;
 
 import androidx.annotation.RequiresApi;
 
-import com.android.permission.util.PackageUtils;
+import com.android.safetycenter.resources.SafetyCenterResourcesContext;
+
+import java.util.Arrays;
 
 /** Helps build or retrieve {@link PendingIntent} instances. */
 @RequiresApi(TIRAMISU)
 final class PendingIntentFactory {
 
     private static final String TAG = "PendingIntentFactory";
-    private static final String ANDROID_LOCK_SCREEN_SOURCE_ID = "AndroidLockScreen";
+
     private static final int DEFAULT_REQUEST_CODE = 0;
-    private static final int ANDROID_LOCK_SCREEN_ICON_ACTION_REQ_CODE = 86;
+
+    private static final String IS_SETTINGS_HOMEPAGE = "is_from_settings_homepage";
+
+    private static final String ANDROID_LOCK_SCREEN_SOURCE_ID = "AndroidLockScreen";
+    // Arbitrary values to construct PendingIntents that are guaranteed not to be equal due to
+    // these request codes not being equal. The values match the ones in Settings QPR, in case we
+    // ever end up using these request codes in QPR.
+    private static final int ANDROID_LOCK_SCREEN_ENTRY_REQ_CODE = 1;
+    private static final int ANDROID_LOCK_SCREEN_ICON_ACTION_REQ_CODE = 2;
 
     @NonNull private final Context mContext;
+    @NonNull private final SafetyCenterResourcesContext mSafetyCenterResourcesContext;
 
-    PendingIntentFactory(@NonNull Context context) {
+    PendingIntentFactory(
+            @NonNull Context context,
+            @NonNull SafetyCenterResourcesContext safetyCenterResourcesContext) {
         mContext = context;
+        mSafetyCenterResourcesContext = safetyCenterResourcesContext;
     }
 
     /**
@@ -57,11 +73,14 @@
      * <p>If the given {@code intentAction} doesn't resolve implicitly, it will be matched
      * explicitly using the given {@code packageName}.
      *
+     * <p>The {@code PendingIntent} is associated with a specific source given by {@code sourceId}.
+     *
      * <p>Returns {@code null} if the required {@link PendingIntent} cannot be created or if there
      * is no valid target for the given {@code intentAction}.
      */
     @Nullable
     PendingIntent getPendingIntent(
+            @NonNull String sourceId,
             @Nullable String intentAction,
             @NonNull String packageName,
             @UserIdInt int userId,
@@ -73,7 +92,7 @@
         if (packageContext == null) {
             return null;
         }
-        Intent intent = createIntent(intentAction, packageName, userId, isQuietModeEnabled);
+        Intent intent = createIntent(packageContext, sourceId, intentAction, isQuietModeEnabled);
         if (intent == null) {
             return null;
         }
@@ -81,20 +100,23 @@
     }
 
     /**
-     * Potentially overrides the Settings IconAction PendingIntent for the AndroidLockScreen source.
+     * Potentially overrides the Settings {@link PendingIntent}s for the AndroidLockScreen source.
      *
-     * <p>This is done because of a bug in the Settings app where the PendingIntent created ends up
-     * referencing the one from the main entry. The reason for this is that PendingIntent instances
-     * are cached and keyed by an object which does not take into account the underlying intent
-     * extras; and these two intents only differ by the extras that they set. We fix this issue by
-     * recreating the desired Intent and PendingIntent here, using a specific request code for the
-     * PendingIntent to ensure a new instance is created (the key does take into account the request
-     * code).
+     * <p>This is done because of a bug in the Settings app where the {@link PendingIntent}s created
+     * end up referencing either the {@link SafetyCenterEntry#getPendingIntent()} or the {@link
+     * SafetyCenterEntry.IconAction#getPendingIntent()}. The reason for this is that {@link
+     * PendingIntent} instances are cached and keyed by an object which does not take into account
+     * the underlying {@link Intent} extras; and these two {@link Intent}s only differ by the extras
+     * that they set.
+     *
+     * <p>We fix this issue by recreating the desired {@link PendingIntent} manually here, using
+     * different request codes for the different {@link PendingIntent}s to ensure new instances are
+     * created (the key does take into account the request code).
      */
-    @NonNull
-    PendingIntent getIconActionPendingIntent(
-            @NonNull String sourceId, @NonNull PendingIntent pendingIntent) {
-        if (!ANDROID_LOCK_SCREEN_SOURCE_ID.equals(sourceId)) {
+    @Nullable
+    PendingIntent maybeOverridePendingIntent(
+            @NonNull String sourceId, @Nullable PendingIntent pendingIntent, boolean isIconAction) {
+        if (!ANDROID_LOCK_SCREEN_SOURCE_ID.equals(sourceId) || pendingIntent == null) {
             return pendingIntent;
         }
         if (!SafetyCenterFlags.getReplaceLockScreenIconAction()) {
@@ -102,85 +124,180 @@
         }
         String settingsPackageName = pendingIntent.getCreatorPackage();
         int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
-        Context packageContext = createPackageContextAsUser(settingsPackageName, userId);
-        if (packageContext == null) {
+        Context settingsPackageContext = createPackageContextAsUser(settingsPackageName, userId);
+        if (settingsPackageContext == null) {
             return pendingIntent;
         }
-        Resources settingsResources = packageContext.getResources();
+        if (hasFixedSettingsIssue(settingsPackageContext)) {
+            return pendingIntent;
+        }
+        PendingIntent suspectPendingIntent =
+                getActivityPendingIntent(
+                        settingsPackageContext,
+                        DEFAULT_REQUEST_CODE,
+                        newBaseLockScreenIntent(settingsPackageName),
+                        PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_NO_CREATE);
+        if (suspectPendingIntent == null) {
+            // Nothing was cached.
+            return pendingIntent;
+        }
+        if (!suspectPendingIntent.equals(pendingIntent)) {
+            // The pending intent is not hitting this caching issue, so we should skip the override.
+            return pendingIntent;
+        }
+        // We’re most likely hitting the caching issue described in this method’s documentation, so
+        // we should ensure we create brand new pending intents where applicable by using different
+        // request codes. We only perform this override for the applicable pending intents.
+        // This is important because there are scenarios where the Settings app provides different
+        // pending intents (e.g. in the work profile), and in this case we shouldn't override them.
+        if (isIconAction) {
+            Log.w(
+                    TAG,
+                    "Replacing " + ANDROID_LOCK_SCREEN_SOURCE_ID + " icon action pending intent");
+            return getActivityPendingIntent(
+                    settingsPackageContext,
+                    ANDROID_LOCK_SCREEN_ICON_ACTION_REQ_CODE,
+                    newLockScreenIconActionIntent(settingsPackageName));
+        }
+        Log.w(TAG, "Replacing " + ANDROID_LOCK_SCREEN_SOURCE_ID + " entry or issue pending intent");
+        return getActivityPendingIntent(
+                settingsPackageContext,
+                ANDROID_LOCK_SCREEN_ENTRY_REQ_CODE,
+                newLockScreenIntent(settingsPackageName));
+    }
+
+    private static boolean hasFixedSettingsIssue(@NonNull Context settingsPackageContext) {
+        Resources settingsResources = settingsPackageContext.getResources();
         int hasSettingsFixedIssueResourceId =
                 settingsResources.getIdentifier(
                         "config_isSafetyCenterLockScreenPendingIntentFixed",
                         "bool",
-                        settingsPackageName);
+                        settingsPackageContext.getPackageName());
         if (hasSettingsFixedIssueResourceId != Resources.ID_NULL) {
-            boolean hasSettingsFixedIssue =
-                    settingsResources.getBoolean(hasSettingsFixedIssueResourceId);
-            if (hasSettingsFixedIssue) {
-                return pendingIntent;
-            }
+            return settingsResources.getBoolean(hasSettingsFixedIssueResourceId);
         }
-        Intent intent =
-                new Intent(Intent.ACTION_MAIN)
-                        .setComponent(
-                                new ComponentName(
-                                        settingsPackageName, settingsPackageName + ".SubSettings"))
-                        .putExtra(
-                                ":settings:show_fragment",
-                                settingsPackageName + ".security.screenlock.ScreenLockSettings")
-                        .putExtra(":settings:source_metrics", 1917)
-                        .putExtra("page_transition_type", 0);
-        PendingIntent offendingPendingIntent =
-                getActivityPendingIntent(packageContext, DEFAULT_REQUEST_CODE, intent);
-        if (!offendingPendingIntent.equals(pendingIntent)) {
-            return pendingIntent;
-        }
-        // If creating that PendingIntent with request code 0 returns the same value as the
-        // PendingIntent that was sent to Safety Center, then we’re most likely hitting the caching
-        // issue described in this method’s documentation.
-        // i.e. the intent action and component of the cached PendingIntent are the same, but the
-        // extras are actually different so we should ensure we create a brand new PendingIntent by
-        // changing the request code.
-        return getActivityPendingIntent(
-                packageContext, ANDROID_LOCK_SCREEN_ICON_ACTION_REQ_CODE, intent);
+        return false;
+    }
+
+    @NonNull
+    private static Intent newBaseLockScreenIntent(@NonNull String settingsPackageName) {
+        return new Intent(Intent.ACTION_MAIN)
+                .setComponent(
+                        new ComponentName(
+                                settingsPackageName, settingsPackageName + ".SubSettings"))
+                .putExtra(":settings:source_metrics", 1917);
+    }
+
+    @NonNull
+    private static Intent newLockScreenIntent(@NonNull String settingsPackageName) {
+        String targetFragment =
+                settingsPackageName + ".password.ChooseLockGeneric$ChooseLockGenericFragment";
+        return newBaseLockScreenIntent(settingsPackageName)
+                .putExtra(":settings:show_fragment", targetFragment)
+                .putExtra("page_transition_type", 1);
+    }
+
+    @NonNull
+    private static Intent newLockScreenIconActionIntent(@NonNull String settingsPackageName) {
+        String targetFragment = settingsPackageName + ".security.screenlock.ScreenLockSettings";
+        return newBaseLockScreenIntent(settingsPackageName)
+                .putExtra(":settings:show_fragment", targetFragment)
+                .putExtra("page_transition_type", 0);
     }
 
     @Nullable
     private Intent createIntent(
+            @NonNull Context packageContext,
+            @NonNull String sourceId,
             @NonNull String intentAction,
-            @NonNull String packageName,
-            @UserIdInt int userId,
             boolean isQuietModeEnabled) {
         Intent intent = new Intent(intentAction);
+
+        if (shouldAddSettingsHomepageExtra(sourceId)) {
+            // Identify this intent as coming from Settings. Because this intent is actually coming
+            // from Safety Center, which is served by PermissionController, this is useful to
+            // indicate that it is presented as part of the Settings app.
+            //
+            // In particular, the AOSP Settings app uses this to ensure that two-pane mode works
+            // correctly.
+            intent.putExtra(IS_SETTINGS_HOMEPAGE, true);
+        }
+
         // queryIntentActivities does not return any activity when the work profile is in quiet
         // mode, even though it opens the quiet mode dialog. So, we assume that the intent will
         // resolve to this dialog.
         if (isQuietModeEnabled) {
             return intent;
         }
-        if (intentResolves(intent, userId)) {
+        if (intentResolves(packageContext, intent)) {
             return intent;
         }
-        intent.setPackage(packageName);
-        if (intentResolves(intent, userId)) {
+        intent.setPackage(packageContext.getPackageName());
+        if (intentResolves(packageContext, intent)) {
             return intent;
         }
         return null;
     }
 
-    private boolean intentResolves(@NonNull Intent intent, @UserIdInt int userId) {
-        return !PackageUtils.queryUnfilteredIntentActivitiesAsUser(intent, 0, userId, mContext)
+    private boolean shouldAddSettingsHomepageExtra(@NonNull String sourceId) {
+        return Arrays.asList(
+                        mSafetyCenterResourcesContext
+                                .getStringByName("config_useSettingsHomepageIntentExtra")
+                                .split(","))
+                .contains(sourceId);
+    }
+
+    private static boolean intentResolves(@NonNull Context packageContext, @NonNull Intent intent) {
+        return !packageContext
+                .getPackageManager()
+                .queryIntentActivities(intent, ResolveInfoFlags.of(0))
                 .isEmpty();
     }
 
     @NonNull
     private static PendingIntent getActivityPendingIntent(
             @NonNull Context packageContext, int requestCode, @NonNull Intent intent) {
+        return getActivityPendingIntent(
+                packageContext, requestCode, intent, PendingIntent.FLAG_IMMUTABLE);
+    }
+
+    /**
+     * Creates a {@link PendingIntent} to start an Activity from the given {@code packageContext}.
+     *
+     * <p>This function can only return {@code null} if the {@link PendingIntent#FLAG_NO_CREATE}
+     * flag is passed in.
+     */
+    @Nullable
+    static PendingIntent getActivityPendingIntent(
+            @NonNull Context packageContext, int requestCode, @NonNull Intent intent, int flags) {
         // This call requires Binder identity to be cleared for getIntentSender() to be allowed to
         // send as another package.
         final long callingId = Binder.clearCallingIdentity();
         try {
-            return PendingIntent.getActivity(
-                    packageContext, requestCode, intent, PendingIntent.FLAG_IMMUTABLE);
+            return PendingIntent.getActivity(packageContext, requestCode, intent, flags);
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    /**
+     * Creates a non-protected broadcast {@link PendingIntent} which can only be received by the
+     * system. Use this method to create PendingIntents to be received by Context-registered
+     * receivers, for example for notification-related callbacks.
+     *
+     * <p>{@code flags} must include {@link PendingIntent#FLAG_IMMUTABLE}
+     */
+    @Nullable
+    static PendingIntent getNonProtectedSystemOnlyBroadcastPendingIntent(
+            @NonNull Context context, int requestCode, @NonNull Intent intent, int flags) {
+        if ((flags & PendingIntent.FLAG_IMMUTABLE) == 0) {
+            throw new IllegalArgumentException("flags must include FLAG_IMMUTABLE");
+        }
+        intent.setPackage("android");
+        // This call is needed to be allowed to send the broadcast as the "android" package.
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            return PendingIntent.getBroadcast(context, requestCode, intent, flags);
         } finally {
             Binder.restoreCallingIdentity(callingId);
         }
diff --git a/service/java/com/android/safetycenter/RefreshReasons.java b/service/java/com/android/safetycenter/RefreshReasons.java
index c5f860a..ee318c7 100644
--- a/service/java/com/android/safetycenter/RefreshReasons.java
+++ b/service/java/com/android/safetycenter/RefreshReasons.java
@@ -17,21 +17,26 @@
 package com.android.safetycenter;
 
 import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
 import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA;
 import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_GET_DATA;
 import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_DEVICE_LOCALE_CHANGE;
 import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_DEVICE_REBOOT;
 import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_OTHER;
 import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_PAGE_OPEN;
+import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_PERIODIC;
 import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_RESCAN_BUTTON_CLICK;
 import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_SAFETY_CENTER_ENABLED;
 
+import android.annotation.TargetApi;
 import android.safetycenter.SafetyCenterManager.RefreshReason;
 import android.safetycenter.SafetyCenterManager.RefreshRequestType;
 import android.util.Log;
 
 import androidx.annotation.RequiresApi;
 
+import com.android.modules.utils.build.SdkLevel;
+
 /** Helpers to do with {@link RefreshReason}. */
 @RequiresApi(TIRAMISU)
 final class RefreshReasons {
@@ -54,10 +59,14 @@
             case REFRESH_REASON_OTHER:
                 return;
         }
+        if (SdkLevel.isAtLeastU() && refreshReason == REFRESH_REASON_PERIODIC) {
+            return;
+        }
         throw new IllegalArgumentException("Unexpected refresh reason: " + refreshReason);
     }
 
     /** Converts the given {@link RefreshReason} to a {@link RefreshRequestType}. */
+    @TargetApi(UPSIDE_DOWN_CAKE)
     @RefreshRequestType
     static int toRefreshRequestType(@RefreshReason int refreshReason) {
         switch (refreshReason) {
@@ -68,6 +77,7 @@
             case REFRESH_REASON_DEVICE_LOCALE_CHANGE:
             case REFRESH_REASON_SAFETY_CENTER_ENABLED:
             case REFRESH_REASON_OTHER:
+            case REFRESH_REASON_PERIODIC:
                 return EXTRA_REFRESH_REQUEST_TYPE_GET_DATA;
         }
         Log.w(TAG, "Unexpected refresh reason: " + refreshReason);
@@ -77,12 +87,14 @@
     /**
      * Returns {@code true} if the given {@link RefreshReason} corresponds to a background refresh.
      */
+    @TargetApi(UPSIDE_DOWN_CAKE)
     static boolean isBackgroundRefresh(@RefreshReason int refreshReason) {
         switch (refreshReason) {
             case REFRESH_REASON_DEVICE_REBOOT:
             case REFRESH_REASON_DEVICE_LOCALE_CHANGE:
             case REFRESH_REASON_SAFETY_CENTER_ENABLED:
             case REFRESH_REASON_OTHER:
+            case REFRESH_REASON_PERIODIC:
                 return true;
             case REFRESH_REASON_PAGE_OPEN:
             case REFRESH_REASON_RESCAN_BUTTON_CLICK:
diff --git a/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java b/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java
index 4deb642..65f6bdf 100644
--- a/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java
+++ b/service/java/com/android/safetycenter/SafetyCenterBroadcastDispatcher.java
@@ -83,10 +83,15 @@
      * SafetyCenterManager#ACTION_REFRESH_SAFETY_SOURCES}, and returns the associated broadcast id.
      *
      * <p>Returns {@code null} if no broadcast was sent.
+     *
+     * @param safetySourceIds list of IDs to specify the safety sources to be refreshed or a {@code
+     *     null} value to refresh all safety sources.
      */
     @Nullable
     String sendRefreshSafetySources(
-            @RefreshReason int refreshReason, @NonNull UserProfileGroup userProfileGroup) {
+            @RefreshReason int refreshReason,
+            @NonNull UserProfileGroup userProfileGroup,
+            @Nullable List<String> safetySourceIds) {
         List<Broadcast> broadcasts = mSafetyCenterConfigReader.getBroadcasts();
         BroadcastOptions broadcastOptions = createBroadcastOptions();
 
@@ -104,7 +109,8 @@
                             broadcastOptions,
                             refreshReason,
                             userProfileGroup,
-                            broadcastId);
+                            broadcastId,
+                            safetySourceIds);
         }
 
         if (!hasSentAtLeastOneBroadcast) {
@@ -120,7 +126,8 @@
             @NonNull BroadcastOptions broadcastOptions,
             @RefreshReason int refreshReason,
             @NonNull UserProfileGroup userProfileGroup,
-            @NonNull String broadcastId) {
+            @NonNull String broadcastId,
+            @Nullable List<String> requiredSourceIds) {
         boolean hasSentAtLeastOneBroadcast = false;
         int requestType = RefreshReasons.toRefreshRequestType(refreshReason);
         String packageName = broadcast.getPackageName();
@@ -137,6 +144,11 @@
                 sourceIds.removeAll(deniedSourceIds);
             }
 
+            if (requiredSourceIds != null) {
+                sourceIds = new ArrayList<>(sourceIds);
+                sourceIds.retainAll(requiredSourceIds);
+            }
+
             if (sourceIds.isEmpty()) {
                 continue;
             }
diff --git a/service/java/com/android/safetycenter/SafetyCenterConfigReader.java b/service/java/com/android/safetycenter/SafetyCenterConfigReader.java
index 72dbae8..ded5a3b 100644
--- a/service/java/com/android/safetycenter/SafetyCenterConfigReader.java
+++ b/service/java/com/android/safetycenter/SafetyCenterConfigReader.java
@@ -116,6 +116,15 @@
     }
 
     /**
+     * Returns the groups of {@link SafetySource}, filtering out any sources where {@link
+     * SafetySources#isLoggable(SafetySource)} is false (and any resultingly empty groups).
+     */
+    @NonNull
+    List<SafetySourcesGroup> getLoggableSafetySourcesGroups() {
+        return getCurrentConfigInternal().getLoggableSourcesGroups();
+    }
+
+    /**
      * Returns the external {@link SafetySource} associated with the {@code safetySourceId}, if any.
      *
      * <p>The returned {@link SafetySource} can either be associated with the XML or overridden
@@ -182,13 +191,13 @@
     private SafetyCenterConfig readSafetyCenterConfig() {
         InputStream in = mSafetyCenterResourcesContext.getSafetyCenterConfig();
         if (in == null) {
-            Log.e(TAG, "Cannot get safety center config file");
+            Log.e(TAG, "Cannot get safety center config file, safety center will be disabled.");
             return null;
         }
 
         Resources resources = mSafetyCenterResourcesContext.getResources();
         if (resources == null) {
-            Log.e(TAG, "Cannot get safety center resources");
+            Log.e(TAG, "Cannot get safety center resources, safety center will be disabled.");
             return null;
         }
 
@@ -198,7 +207,7 @@
             Log.i(TAG, "SafetyCenterConfig read successfully");
             return safetyCenterConfig;
         } catch (ParseException e) {
-            Log.e(TAG, "Cannot read SafetyCenterConfig", e);
+            Log.e(TAG, "Cannot read SafetyCenterConfig, safety center will be disabled.", e);
             return null;
         }
     }
@@ -218,14 +227,17 @@
 
         @NonNull private final SafetyCenterConfig mConfig;
         @NonNull private final ArrayMap<String, ExternalSafetySource> mExternalSafetySources;
+        @NonNull private final List<SafetySourcesGroup> mLoggableSourcesGroups;
         @NonNull private final List<Broadcast> mBroadcasts;
 
         private SafetyCenterConfigInternal(
                 @NonNull SafetyCenterConfig safetyCenterConfig,
                 @NonNull ArrayMap<String, ExternalSafetySource> externalSafetySources,
+                @NonNull List<SafetySourcesGroup> loggableSourcesGroups,
                 @NonNull List<Broadcast> broadcasts) {
             mConfig = safetyCenterConfig;
             mExternalSafetySources = externalSafetySources;
+            mLoggableSourcesGroups = loggableSourcesGroups;
             mBroadcasts = broadcasts;
         }
 
@@ -240,6 +252,11 @@
         }
 
         @NonNull
+        private List<SafetySourcesGroup> getLoggableSourcesGroups() {
+            return mLoggableSourcesGroups;
+        }
+
+        @NonNull
         private List<Broadcast> getBroadcasts() {
             return mBroadcasts;
         }
@@ -264,6 +281,8 @@
                     + mConfig
                     + ", mExternalSafetySources="
                     + mExternalSafetySources
+                    + ", mLoggableSourcesGroups="
+                    + mLoggableSourcesGroups
                     + ", mBroadcasts="
                     + mBroadcasts
                     + '}';
@@ -275,6 +294,7 @@
             return new SafetyCenterConfigInternal(
                     safetyCenterConfig,
                     extractExternalSafetySources(safetyCenterConfig),
+                    extractLoggableSafetySourcesGroups(safetyCenterConfig),
                     unmodifiableList(extractBroadcasts(safetyCenterConfig)));
         }
 
@@ -295,14 +315,15 @@
                         continue;
                     }
 
-                    boolean hasEntryInRigidGroup =
+                    boolean hasEntryInStatelessGroup =
                             safetySource.getType() == SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC
                                     && safetySourcesGroup.getType()
-                                            == SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID;
+                                            == SafetySourcesGroup
+                                                    .SAFETY_SOURCES_GROUP_TYPE_STATELESS;
 
                     externalSafetySources.put(
                             safetySource.getId(),
-                            new ExternalSafetySource(safetySource, hasEntryInRigidGroup));
+                            new ExternalSafetySource(safetySource, hasEntryInStatelessGroup));
                 }
             }
 
@@ -310,6 +331,35 @@
         }
 
         @NonNull
+        private static List<SafetySourcesGroup> extractLoggableSafetySourcesGroups(
+                @NonNull SafetyCenterConfig safetyCenterConfig) {
+            List<SafetySourcesGroup> originalGroups = safetyCenterConfig.getSafetySourcesGroups();
+            List<SafetySourcesGroup> filteredGroups = new ArrayList<>(originalGroups.size());
+
+            for (int i = 0; i < originalGroups.size(); i++) {
+                SafetySourcesGroup originalGroup = originalGroups.get(i);
+
+                SafetySourcesGroup.Builder filteredGroupBuilder =
+                        SafetySourcesGroups.copyToBuilderWithoutSources(originalGroup);
+                List<SafetySource> originalSources = originalGroup.getSafetySources();
+                for (int j = 0; j < originalSources.size(); j++) {
+                    SafetySource source = originalSources.get(j);
+
+                    if (SafetySources.isLoggable(source)) {
+                        filteredGroupBuilder.addSafetySource(source);
+                    }
+                }
+
+                SafetySourcesGroup filteredGroup = filteredGroupBuilder.build();
+                if (!filteredGroup.getSafetySources().isEmpty()) {
+                    filteredGroups.add(filteredGroup);
+                }
+            }
+
+            return filteredGroups;
+        }
+
+        @NonNull
         private static List<Broadcast> extractBroadcasts(
                 @NonNull SafetyCenterConfig safetyCenterConfig) {
             ArrayMap<String, Broadcast> packageNameToBroadcast = new ArrayMap<>();
@@ -334,14 +384,14 @@
                         broadcasts.add(broadcast);
                     }
                     broadcast.mSourceIdsForProfileParent.add(safetySource.getId());
-                    if (safetySource.isRefreshOnPageOpenAllowed()) {
+                    if (isRefreshOnPageOpenAllowedAfterApplyingOverrides(safetySource)) {
                         broadcast.mSourceIdsForProfileParentOnPageOpen.add(safetySource.getId());
                     }
                     boolean needsManagedProfilesBroadcast =
                             SafetySources.supportsManagedProfiles(safetySource);
                     if (needsManagedProfilesBroadcast) {
                         broadcast.mSourceIdsForManagedProfiles.add(safetySource.getId());
-                        if (safetySource.isRefreshOnPageOpenAllowed()) {
+                        if (isRefreshOnPageOpenAllowedAfterApplyingOverrides(safetySource)) {
                             broadcast.mSourceIdsForManagedProfilesOnPageOpen.add(
                                     safetySource.getId());
                         }
@@ -353,15 +403,22 @@
         }
     }
 
+    private static boolean isRefreshOnPageOpenAllowedAfterApplyingOverrides(
+            SafetySource safetySource) {
+        return safetySource.isRefreshOnPageOpenAllowed()
+                || SafetyCenterFlags.getOverrideRefreshOnPageOpenSourceIds()
+                        .contains(safetySource.getId());
+    }
+
     /** A wrapper class around a {@link SafetySource} that is providing data externally. */
     static final class ExternalSafetySource {
         @NonNull private final SafetySource mSafetySource;
-        @NonNull private final boolean mHasEntryInRigidGroup;
+        @NonNull private final boolean mHasEntryInStatelessGroup;
 
         private ExternalSafetySource(
-                @NonNull SafetySource safetySource, boolean hasEntryInRigidGroup) {
+                @NonNull SafetySource safetySource, boolean hasEntryInStatelessGroup) {
             mSafetySource = safetySource;
-            mHasEntryInRigidGroup = hasEntryInRigidGroup;
+            mHasEntryInStatelessGroup = hasEntryInStatelessGroup;
         }
 
         /** Returns the external {@link SafetySource}. */
@@ -371,11 +428,11 @@
         }
 
         /**
-         * Returns whether the external {@link SafetySource} has an entry in a rigid {@link
+         * Returns whether the external {@link SafetySource} has an entry in a stateless {@link
          * SafetySourcesGroup}.
          */
-        boolean hasEntryInRigidGroup() {
-            return mHasEntryInRigidGroup;
+        boolean hasEntryInStatelessGroup() {
+            return mHasEntryInStatelessGroup;
         }
 
         @Override
@@ -383,13 +440,13 @@
             if (this == o) return true;
             if (!(o instanceof ExternalSafetySource)) return false;
             ExternalSafetySource that = (ExternalSafetySource) o;
-            return mHasEntryInRigidGroup == that.mHasEntryInRigidGroup
+            return mHasEntryInStatelessGroup == that.mHasEntryInStatelessGroup
                     && mSafetySource.equals(that.mSafetySource);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mSafetySource, mHasEntryInRigidGroup);
+            return Objects.hash(mSafetySource, mHasEntryInStatelessGroup);
         }
 
         @Override
@@ -397,8 +454,8 @@
             return "ExternalSafetySource{"
                     + "mSafetySource="
                     + mSafetySource
-                    + ", mHasEntryInRigidGroup="
-                    + mHasEntryInRigidGroup
+                    + ", mHasEntryInStatelessGroup="
+                    + mHasEntryInStatelessGroup
                     + '}';
         }
     }
diff --git a/service/java/com/android/safetycenter/SafetyCenterDataFactory.java b/service/java/com/android/safetycenter/SafetyCenterDataFactory.java
index 281c267..d833fe6 100644
--- a/service/java/com/android/safetycenter/SafetyCenterDataFactory.java
+++ b/service/java/com/android/safetycenter/SafetyCenterDataFactory.java
@@ -19,6 +19,7 @@
 import static android.os.Build.VERSION_CODES.TIRAMISU;
 
 import static java.util.Collections.emptyList;
+import static java.util.Objects.requireNonNull;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -47,7 +48,7 @@
 
 import androidx.annotation.RequiresApi;
 
-import com.android.safetycenter.internaldata.SafetyCenterEntryGroupId;
+import com.android.modules.utils.build.SdkLevel;
 import com.android.safetycenter.internaldata.SafetyCenterEntryId;
 import com.android.safetycenter.internaldata.SafetyCenterIds;
 import com.android.safetycenter.internaldata.SafetyCenterIssueActionId;
@@ -87,19 +88,41 @@
     @NonNull private final SafetyCenterIssueCache mSafetyCenterIssueCache;
     @NonNull private final SafetyCenterRepository mSafetyCenterRepository;
 
+    /** Only available on Android U+. */
+    @Nullable private final SafetyCenterIssueDeduplicator mSafetyCenterIssueDeduplicator;
+
     SafetyCenterDataFactory(
             @NonNull SafetyCenterResourcesContext safetyCenterResourcesContext,
             @NonNull SafetyCenterConfigReader safetyCenterConfigReader,
             @NonNull SafetyCenterRefreshTracker safetyCenterRefreshTracker,
             @NonNull PendingIntentFactory pendingIntentFactory,
             @NonNull SafetyCenterIssueCache safetyCenterIssueCache,
-            @NonNull SafetyCenterRepository safetyCenterRepository) {
+            @NonNull SafetyCenterRepository safetyCenterRepository,
+            @Nullable SafetyCenterIssueDeduplicator safetyCenterIssueDeduplicator) {
         mSafetyCenterResourcesContext = safetyCenterResourcesContext;
         mSafetyCenterConfigReader = safetyCenterConfigReader;
         mSafetyCenterRefreshTracker = safetyCenterRefreshTracker;
         mPendingIntentFactory = pendingIntentFactory;
         mSafetyCenterIssueCache = safetyCenterIssueCache;
         mSafetyCenterRepository = safetyCenterRepository;
+        mSafetyCenterIssueDeduplicator = safetyCenterIssueDeduplicator;
+    }
+
+    /**
+     * Returns a default {@link SafetyCenterData} object to be returned when the API is disabled.
+     */
+    @NonNull
+    static SafetyCenterData getDefaultSafetyCenterData() {
+        SafetyCenterStatus defaultSafetyCenterStatus =
+                new SafetyCenterStatus.Builder("", "")
+                        .setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN)
+                        .build();
+        if (SdkLevel.isAtLeastU()) {
+            return new SafetyCenterData.Builder(defaultSafetyCenterStatus).build();
+        } else {
+            return new SafetyCenterData(
+                    defaultSafetyCenterStatus, emptyList(), emptyList(), emptyList());
+        }
     }
 
     /**
@@ -110,32 +133,25 @@
      * SafetyCenterConfig} is used.
      */
     @NonNull
-    SafetyCenterData getSafetyCenterData(
+    SafetyCenterData assembleSafetyCenterData(
             @NonNull String packageName, @NonNull UserProfileGroup userProfileGroup) {
-        return getSafetyCenterData(
-                mSafetyCenterConfigReader.getSafetySourcesGroups(), packageName, userProfileGroup);
+        return assembleSafetyCenterData(packageName, userProfileGroup, getAllGroups());
     }
 
     /**
-     * Returns a default {@link SafetyCenterData} object to be returned when the API is disabled.
+     * Returns the current {@link SafetyCenterData} for the given {@code packageName} and {@link
+     * UserProfileGroup}, aggregated from {@link SafetySourceData} set by the specified {@link
+     * SafetySourcesGroup}s.
+     *
+     * <p>If a {@link SafetySourceData} was not set, the default value from the {@link
+     * SafetyCenterConfig} is used.
      */
     @NonNull
-    static SafetyCenterData getDefaultSafetyCenterData() {
-        return new SafetyCenterData(
-                new SafetyCenterStatus.Builder("", "")
-                        .setSeverityLevel(SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN)
-                        .build(),
-                emptyList(),
-                emptyList(),
-                emptyList());
-    }
-
-    @NonNull
-    private SafetyCenterData getSafetyCenterData(
-            @NonNull List<SafetySourcesGroup> safetySourcesGroups,
+    SafetyCenterData assembleSafetyCenterData(
             @NonNull String packageName,
-            @NonNull UserProfileGroup userProfileGroup) {
-        List<SafetyCenterIssueWithCategory> safetyCenterIssuesWithCategories = new ArrayList<>();
+            @NonNull UserProfileGroup userProfileGroup,
+            @NonNull List<SafetySourcesGroup> safetySourcesGroups) {
+        List<SafetyCenterIssueExtended> safetyCenterIssuesExtended = new ArrayList<>();
         List<SafetyCenterEntryOrGroup> safetyCenterEntryOrGroups = new ArrayList<>();
         List<SafetyCenterStaticEntryGroup> safetyCenterStaticEntryGroups = new ArrayList<>();
         SafetyCenterOverallState safetyCenterOverallState = new SafetyCenterOverallState();
@@ -143,14 +159,10 @@
         for (int i = 0; i < safetySourcesGroups.size(); i++) {
             SafetySourcesGroup safetySourcesGroup = safetySourcesGroups.get(i);
 
-            addSafetyCenterIssues(
-                    safetyCenterOverallState,
-                    safetyCenterIssuesWithCategories,
-                    safetySourcesGroup,
-                    userProfileGroup);
+            addSafetyCenterIssues(safetyCenterIssuesExtended, safetySourcesGroup, userProfileGroup);
             int safetySourcesGroupType = safetySourcesGroup.getType();
             switch (safetySourcesGroupType) {
-                case SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE:
+                case SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL:
                     addSafetyCenterEntryGroup(
                             safetyCenterOverallState,
                             safetyCenterEntryOrGroups,
@@ -158,7 +170,7 @@
                             packageName,
                             userProfileGroup);
                     break;
-                case SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID:
+                case SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS:
                     addSafetyCenterStaticEntryGroup(
                             safetyCenterOverallState,
                             safetyCenterStaticEntryGroups,
@@ -174,20 +186,37 @@
             }
         }
 
-        safetyCenterIssuesWithCategories.sort(SAFETY_CENTER_ISSUES_BY_SEVERITY_DESCENDING);
+        safetyCenterIssuesExtended.sort(SAFETY_CENTER_ISSUES_BY_SEVERITY_DESCENDING);
 
-        List<SafetyCenterIssue> safetyCenterIssues =
-                new ArrayList<>(safetyCenterIssuesWithCategories.size());
-        for (int i = 0; i < safetyCenterIssuesWithCategories.size(); i++) {
-            safetyCenterIssues.add(safetyCenterIssuesWithCategories.get(i).getSafetyCenterIssue());
+        if (SdkLevel.isAtLeastU() && mSafetyCenterIssueDeduplicator != null) {
+            mSafetyCenterIssueDeduplicator.deduplicateIssues(safetyCenterIssuesExtended);
+        }
+
+        List<SafetyCenterIssue> safetyCenterIssues = new ArrayList<>();
+        List<SafetyCenterIssue> safetyCenterDismissedIssues = new ArrayList<>();
+        SafetyCenterIssueExtended topNonDismissedIssueExtended = null;
+
+        for (int i = 0; i < safetyCenterIssuesExtended.size(); i++) {
+            SafetyCenterIssueExtended issueExtended = safetyCenterIssuesExtended.get(i);
+            if (mSafetyCenterIssueCache.isIssueDismissed(issueExtended)) {
+                safetyCenterDismissedIssues.add(issueExtended.getSafetyCenterIssue());
+            } else {
+                safetyCenterIssues.add(issueExtended.getSafetyCenterIssue());
+                safetyCenterOverallState.addIssueOverallSeverityLevel(
+                        toSafetyCenterStatusOverallSeverityLevel(
+                                issueExtended.getSafetySourceIssueSeverityLevel()));
+                if (topNonDismissedIssueExtended == null) {
+                    topNonDismissedIssueExtended = issueExtended;
+                }
+            }
         }
 
         int refreshStatus = mSafetyCenterRefreshTracker.getRefreshStatus();
-        return new SafetyCenterData(
+        SafetyCenterStatus safetyCenterStatus =
                 new SafetyCenterStatus.Builder(
                                 getSafetyCenterStatusTitle(
                                         safetyCenterOverallState.getOverallSeverityLevel(),
-                                        safetyCenterIssuesWithCategories,
+                                        topNonDismissedIssueExtended,
                                         refreshStatus,
                                         safetyCenterOverallState.hasSettingsToReview()),
                                 getSafetyCenterStatusSummary(
@@ -197,15 +226,39 @@
                                         safetyCenterOverallState.hasSettingsToReview()))
                         .setSeverityLevel(safetyCenterOverallState.getOverallSeverityLevel())
                         .setRefreshStatus(refreshStatus)
-                        .build(),
-                safetyCenterIssues,
-                safetyCenterEntryOrGroups,
-                safetyCenterStaticEntryGroups);
+                        .build();
+
+        if (SdkLevel.isAtLeastU()) {
+            SafetyCenterData.Builder builder = new SafetyCenterData.Builder(safetyCenterStatus);
+            for (int i = 0; i < safetyCenterIssues.size(); i++) {
+                builder.addIssue(safetyCenterIssues.get(i));
+            }
+            for (int i = 0; i < safetyCenterEntryOrGroups.size(); i++) {
+                builder.addEntryOrGroup(safetyCenterEntryOrGroups.get(i));
+            }
+            for (int i = 0; i < safetyCenterStaticEntryGroups.size(); i++) {
+                builder.addStaticEntryGroup(safetyCenterStaticEntryGroups.get(i));
+            }
+            for (int i = 0; i < safetyCenterDismissedIssues.size(); i++) {
+                builder.addDismissedIssue(safetyCenterDismissedIssues.get(i));
+            }
+            return builder.build();
+        } else {
+            return new SafetyCenterData(
+                    safetyCenterStatus,
+                    safetyCenterIssues,
+                    safetyCenterEntryOrGroups,
+                    safetyCenterStaticEntryGroups);
+        }
+    }
+
+    @NonNull
+    private List<SafetySourcesGroup> getAllGroups() {
+        return mSafetyCenterConfigReader.getSafetySourcesGroups();
     }
 
     private void addSafetyCenterIssues(
-            @NonNull SafetyCenterOverallState safetyCenterOverallState,
-            @NonNull List<SafetyCenterIssueWithCategory> safetyCenterIssuesWithCategories,
+            @NonNull List<SafetyCenterIssueExtended> safetyCenterIssues,
             @NonNull SafetySourcesGroup safetySourcesGroup,
             @NonNull UserProfileGroup userProfileGroup) {
         List<SafetySource> safetySources = safetySourcesGroup.getSafetySources();
@@ -217,9 +270,9 @@
             }
 
             addSafetyCenterIssues(
-                    safetyCenterOverallState,
-                    safetyCenterIssuesWithCategories,
+                    safetyCenterIssues,
                     safetySource,
+                    safetySourcesGroup,
                     userProfileGroup.getProfileParentUserId());
 
             if (!SafetySources.supportsManagedProfiles(safetySource)) {
@@ -232,18 +285,18 @@
                 int managedRunningProfileUserId = managedRunningProfilesUserIds[j];
 
                 addSafetyCenterIssues(
-                        safetyCenterOverallState,
-                        safetyCenterIssuesWithCategories,
+                        safetyCenterIssues,
                         safetySource,
+                        safetySourcesGroup,
                         managedRunningProfileUserId);
             }
         }
     }
 
     private void addSafetyCenterIssues(
-            @NonNull SafetyCenterOverallState safetyCenterOverallState,
-            @NonNull List<SafetyCenterIssueWithCategory> safetyCenterIssuesWithCategories,
+            @NonNull List<SafetyCenterIssueExtended> safetyCenterIssues,
             @NonNull SafetySource safetySource,
+            @NonNull SafetySourcesGroup safetySourcesGroup,
             @UserIdInt int userId) {
         SafetySourceKey key = SafetySourceKey.of(safetySource.getId(), userId);
         SafetySourceData safetySourceData = mSafetyCenterRepository.getSafetySourceData(key);
@@ -256,17 +309,19 @@
         for (int i = 0; i < safetySourceIssues.size(); i++) {
             SafetySourceIssue safetySourceIssue = safetySourceIssues.get(i);
             SafetyCenterIssue safetyCenterIssue =
-                    toSafetyCenterIssue(safetySourceIssue, safetySource, userId);
+                    toSafetyCenterIssue(
+                            safetySourceIssue, safetySource, safetySourcesGroup, userId);
 
-            if (safetyCenterIssue == null) {
-                continue;
+            SafetyCenterIssueExtended.Builder issueExtendedBuilder =
+                    new SafetyCenterIssueExtended.Builder(
+                            safetyCenterIssue,
+                            safetySourceIssue.getIssueCategory(),
+                            safetySourceIssue.getSeverityLevel());
+            if (SdkLevel.isAtLeastU()) {
+                issueExtendedBuilder.setDeduplicationGroup(safetySource.getDeduplicationGroup());
+                issueExtendedBuilder.setDeduplicationId(safetySourceIssue.getDeduplicationId());
             }
-
-            safetyCenterOverallState.addIssueOverallSeverityLevel(
-                    toSafetyCenterStatusOverallSeverityLevel(safetySourceIssue.getSeverityLevel()));
-            safetyCenterIssuesWithCategories.add(
-                    SafetyCenterIssueWithCategory.create(
-                            safetyCenterIssue, safetySourceIssue.getIssueCategory()));
+            safetyCenterIssues.add(issueExtendedBuilder.build());
         }
     }
 
@@ -274,6 +329,7 @@
     private SafetyCenterIssue toSafetyCenterIssue(
             @NonNull SafetySourceIssue safetySourceIssue,
             @NonNull SafetySource safetySource,
+            @NonNull SafetySourcesGroup safetySourcesGroup,
             @UserIdInt int userId) {
         SafetyCenterIssueId safetyCenterIssueId =
                 SafetyCenterIssueId.newBuilder()
@@ -286,12 +342,6 @@
                         .setIssueTypeId(safetySourceIssue.getIssueTypeId())
                         .build();
 
-        if (mSafetyCenterIssueCache.isIssueDismissed(
-                safetyCenterIssueId.getSafetyCenterIssueKey(),
-                safetySourceIssue.getSeverityLevel())) {
-            return null;
-        }
-
         List<SafetySourceIssue.Action> safetySourceIssueActions = safetySourceIssue.getActions();
         List<SafetyCenterIssue.Action> safetyCenterIssueActions =
                 new ArrayList<>(safetySourceIssueActions.size());
@@ -306,16 +356,26 @@
 
         int safetyCenterIssueSeverityLevel =
                 toSafetyCenterIssueSeverityLevel(safetySourceIssue.getSeverityLevel());
-        return new SafetyCenterIssue.Builder(
-                        SafetyCenterIds.encodeToString(safetyCenterIssueId),
-                        safetySourceIssue.getTitle(),
-                        safetySourceIssue.getSummary())
-                .setSeverityLevel(safetyCenterIssueSeverityLevel)
-                .setShouldConfirmDismissal(
-                        safetyCenterIssueSeverityLevel > SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK)
-                .setSubtitle(safetySourceIssue.getSubtitle())
-                .setActions(safetyCenterIssueActions)
-                .build();
+        SafetyCenterIssue.Builder safetyCenterIssueBuilder =
+                new SafetyCenterIssue.Builder(
+                                SafetyCenterIds.encodeToString(safetyCenterIssueId),
+                                safetySourceIssue.getTitle(),
+                                safetySourceIssue.getSummary())
+                        .setSeverityLevel(safetyCenterIssueSeverityLevel)
+                        .setShouldConfirmDismissal(
+                                safetyCenterIssueSeverityLevel
+                                        > SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK)
+                        .setSubtitle(safetySourceIssue.getSubtitle())
+                        .setActions(safetyCenterIssueActions);
+        if (SdkLevel.isAtLeastU()) {
+            CharSequence issueAttributionTitle =
+                    TextUtils.isEmpty(safetySourceIssue.getAttributionTitle())
+                            ? mSafetyCenterResourcesContext.getOptionalString(
+                                    safetySourcesGroup.getTitleResId())
+                            : safetySourceIssue.getAttributionTitle();
+            safetyCenterIssueBuilder.setAttributionTitle(issueAttributionTitle);
+        }
+        return safetyCenterIssueBuilder.build();
     }
 
     @NonNull
@@ -327,10 +387,15 @@
                         .setSafetyCenterIssueKey(safetyCenterIssueKey)
                         .setSafetySourceIssueActionId(safetySourceIssueAction.getId())
                         .build();
+        PendingIntent issueActionPendingIntent =
+                mPendingIntentFactory.maybeOverridePendingIntent(
+                        safetyCenterIssueKey.getSafetySourceId(),
+                        safetySourceIssueAction.getPendingIntent(),
+                        false);
         return new SafetyCenterIssue.Action.Builder(
                         SafetyCenterIds.encodeToString(safetyCenterIssueActionId),
                         safetySourceIssueAction.getLabel(),
-                        safetySourceIssueAction.getPendingIntent())
+                        requireNonNull(issueActionPendingIntent))
                 .setSuccessMessage(safetySourceIssueAction.getSuccessMessage())
                 .setIsInFlight(mSafetyCenterRepository.actionIsInFlight(safetyCenterIssueActionId))
                 .setWillResolve(safetySourceIssueAction.willResolve())
@@ -390,22 +455,18 @@
             return;
         }
 
-        if (entries.size() == 1) {
+        if (!SafetyCenterFlags.getShowSubpages() && entries.size() == 1) {
             safetyCenterEntryOrGroups.add(new SafetyCenterEntryOrGroup(entries.get(0)));
             return;
         }
 
-        SafetyCenterEntryGroupId safetyCenterEntryGroupId =
-                SafetyCenterEntryGroupId.newBuilder()
-                        .setSafetySourcesGroupId(safetySourcesGroup.getId())
-                        .build();
         CharSequence groupSummary =
                 getSafetyCenterEntryGroupSummary(
                         safetySourcesGroup, groupSafetyCenterEntryLevel, entries);
         safetyCenterEntryOrGroups.add(
                 new SafetyCenterEntryOrGroup(
                         new SafetyCenterEntryGroup.Builder(
-                                        SafetyCenterIds.encodeToString(safetyCenterEntryGroupId),
+                                        safetySourcesGroup.getId(),
                                         mSafetyCenterResourcesContext.getString(
                                                 safetySourcesGroup.getTitleResId()))
                                 .setSeverityLevel(groupSafetyCenterEntryLevel)
@@ -561,64 +622,67 @@
                 SafetySourceStatus safetySourceStatus =
                         getSafetySourceStatus(mSafetyCenterRepository.getSafetySourceData(key));
                 boolean defaultEntryDueToQuietMode = isUserManaged && !isManagedUserRunning;
-                if (safetySourceStatus != null && !defaultEntryDueToQuietMode) {
-                    PendingIntent pendingIntent = safetySourceStatus.getPendingIntent();
-                    boolean enabled = safetySourceStatus.isEnabled();
-                    if (pendingIntent == null) {
-                        pendingIntent =
-                                mPendingIntentFactory.getPendingIntent(
-                                        safetySource.getIntentAction(),
-                                        safetySource.getPackageName(),
-                                        userId,
-                                        false);
-                        enabled = enabled && pendingIntent != null;
-                    }
-                    SafetyCenterEntryId safetyCenterEntryId =
-                            SafetyCenterEntryId.newBuilder()
-                                    .setSafetySourceId(safetySource.getId())
-                                    .setUserId(userId)
-                                    .build();
-                    int severityUnspecifiedIconType =
-                            SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION;
-                    int severityLevel =
-                            enabled
-                                    ? toSafetyCenterEntrySeverityLevel(
-                                            safetySourceStatus.getSeverityLevel())
-                                    : SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED;
-                    SafetyCenterEntry.Builder builder =
-                            new SafetyCenterEntry.Builder(
-                                            SafetyCenterIds.encodeToString(safetyCenterEntryId),
-                                            safetySourceStatus.getTitle())
-                                    .setSeverityLevel(severityLevel)
-                                    .setSummary(safetySourceStatus.getSummary())
-                                    .setEnabled(enabled)
-                                    .setSeverityUnspecifiedIconType(severityUnspecifiedIconType)
-                                    .setPendingIntent(pendingIntent);
-                    SafetySourceStatus.IconAction iconAction = safetySourceStatus.getIconAction();
-                    if (iconAction == null) {
-                        return builder.build();
-                    }
-                    PendingIntent iconActionPendingIntent =
-                            mPendingIntentFactory.getIconActionPendingIntent(
-                                    safetySource.getId(), iconAction.getPendingIntent());
-                    builder.setIconAction(
-                            new SafetyCenterEntry.IconAction(
-                                    toSafetyCenterEntryIconActionType(iconAction.getIconType()),
-                                    iconActionPendingIntent));
+                if (safetySourceStatus == null || defaultEntryDueToQuietMode) {
+                    return toDefaultSafetyCenterEntry(
+                            safetySource,
+                            safetySource.getPackageName(),
+                            SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNKNOWN,
+                            SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION,
+                            userId,
+                            isUserManaged,
+                            isManagedUserRunning);
+                }
+                PendingIntent entryPendingIntent = safetySourceStatus.getPendingIntent();
+                boolean enabled = safetySourceStatus.isEnabled();
+                if (entryPendingIntent == null) {
+                    entryPendingIntent =
+                            mPendingIntentFactory.getPendingIntent(
+                                    safetySource.getId(),
+                                    safetySource.getIntentAction(),
+                                    safetySource.getPackageName(),
+                                    userId,
+                                    false);
+                    enabled = enabled && entryPendingIntent != null;
+                }
+                SafetyCenterEntryId safetyCenterEntryId =
+                        SafetyCenterEntryId.newBuilder()
+                                .setSafetySourceId(safetySource.getId())
+                                .setUserId(userId)
+                                .build();
+                int severityUnspecifiedIconType =
+                        SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION;
+                int severityLevel =
+                        enabled
+                                ? toSafetyCenterEntrySeverityLevel(
+                                        safetySourceStatus.getSeverityLevel())
+                                : SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED;
+                SafetyCenterEntry.Builder builder =
+                        new SafetyCenterEntry.Builder(
+                                        SafetyCenterIds.encodeToString(safetyCenterEntryId),
+                                        safetySourceStatus.getTitle())
+                                .setSeverityLevel(severityLevel)
+                                .setSummary(safetySourceStatus.getSummary())
+                                .setEnabled(enabled)
+                                .setSeverityUnspecifiedIconType(severityUnspecifiedIconType)
+                                .setPendingIntent(
+                                        mPendingIntentFactory.maybeOverridePendingIntent(
+                                                safetySource.getId(), entryPendingIntent, false));
+                SafetySourceStatus.IconAction iconAction = safetySourceStatus.getIconAction();
+                if (iconAction == null) {
                     return builder.build();
                 }
-                return toDefaultSafetyCenterEntry(
-                        safetySource,
-                        safetySource.getPackageName(),
-                        SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNKNOWN,
-                        SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION,
-                        userId,
-                        isUserManaged,
-                        isManagedUserRunning);
+                PendingIntent iconActionPendingIntent =
+                        mPendingIntentFactory.maybeOverridePendingIntent(
+                                safetySource.getId(), iconAction.getPendingIntent(), true);
+                return builder.setIconAction(
+                                new SafetyCenterEntry.IconAction(
+                                        toSafetyCenterEntryIconActionType(iconAction.getIconType()),
+                                        requireNonNull(iconActionPendingIntent)))
+                        .build();
             case SafetySource.SAFETY_SOURCE_TYPE_STATIC:
                 return toDefaultSafetyCenterEntry(
                         safetySource,
-                        defaultPackageName,
+                        getStaticSourcePackageNameOrDefault(safetySource, defaultPackageName),
                         SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED,
                         SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON,
                         userId,
@@ -652,7 +716,11 @@
         boolean isQuietModeEnabled = isUserManaged && !isManagedUserRunning;
         PendingIntent pendingIntent =
                 mPendingIntentFactory.getPendingIntent(
-                        safetySource.getIntentAction(), packageName, userId, isQuietModeEnabled);
+                        safetySource.getId(),
+                        safetySource.getIntentAction(),
+                        packageName,
+                        userId,
+                        isQuietModeEnabled);
         boolean enabled =
                 pendingIntent != null && !SafetySources.isDefaultEntryDisabled(safetySource);
         CharSequence title =
@@ -724,6 +792,10 @@
             }
         }
 
+        if (staticEntries.isEmpty()) {
+            return;
+        }
+
         safetyCenterStaticEntryGroups.add(
                 new SafetyCenterStaticEntryGroup(
                         mSafetyCenterResourcesContext.getString(safetySourcesGroup.getTitleResId()),
@@ -795,7 +867,7 @@
             case SafetySource.SAFETY_SOURCE_TYPE_STATIC:
                 return toDefaultSafetyCenterStaticEntry(
                         safetySource,
-                        defaultPackageName,
+                        getStaticSourcePackageNameOrDefault(safetySource, defaultPackageName),
                         userId,
                         isUserManaged,
                         isManagedUserRunning);
@@ -817,7 +889,11 @@
         boolean isQuietModeEnabled = isUserManaged && !isManagedUserRunning;
         PendingIntent pendingIntent =
                 mPendingIntentFactory.getPendingIntent(
-                        safetySource.getIntentAction(), packageName, userId, isQuietModeEnabled);
+                        safetySource.getId(),
+                        safetySource.getIntentAction(),
+                        packageName,
+                        userId,
+                        isQuietModeEnabled);
 
         if (pendingIntent == null) {
             // TODO(b/222838784): Decide strategy for static entries when the intent is null.
@@ -857,6 +933,19 @@
         return safetySourceData.getStatus();
     }
 
+    @NonNull
+    private static String getStaticSourcePackageNameOrDefault(
+            @NonNull SafetySource safetySource, @NonNull String defaultPackageName) {
+        if (!SdkLevel.isAtLeastU()) {
+            return defaultPackageName;
+        }
+        String sourcePackageName = safetySource.getOptionalPackageName();
+        if (sourcePackageName == null) {
+            return defaultPackageName;
+        }
+        return sourcePackageName;
+    }
+
     @SafetyCenterStatus.OverallSeverityLevel
     private static int toSafetyCenterStatusOverallSeverityLevel(
             @SafetySourceData.SeverityLevel int safetySourceSeverityLevel) {
@@ -976,7 +1065,7 @@
     @NonNull
     private String getSafetyCenterStatusTitle(
             @SafetyCenterStatus.OverallSeverityLevel int overallSeverityLevel,
-            @NonNull List<SafetyCenterIssueWithCategory> safetyCenterIssuesWithCategories,
+            @Nullable SafetyCenterIssueExtended topNonDismissedIssueExtended,
             @SafetyCenterStatus.RefreshStatus int refreshStatus,
             boolean hasSettingsToReview) {
         boolean overallSeverityUnknown =
@@ -997,16 +1086,22 @@
                         "overall_severity_level_ok_title");
             case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_RECOMMENDATION:
                 return getStatusTitleFromIssueCategories(
-                        safetyCenterIssuesWithCategories,
+                        topNonDismissedIssueExtended,
                         "overall_severity_level_device_recommendation_title",
                         "overall_severity_level_account_recommendation_title",
-                        "overall_severity_level_safety_recommendation_title");
+                        "overall_severity_level_safety_recommendation_title",
+                        "overall_severity_level_data_recommendation_title",
+                        "overall_severity_level_passwords_recommendation_title",
+                        "overall_severity_level_personal_recommendation_title");
             case SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING:
                 return getStatusTitleFromIssueCategories(
-                        safetyCenterIssuesWithCategories,
+                        topNonDismissedIssueExtended,
                         "overall_severity_level_critical_device_warning_title",
                         "overall_severity_level_critical_account_warning_title",
-                        "overall_severity_level_critical_safety_warning_title");
+                        "overall_severity_level_critical_safety_warning_title",
+                        "overall_severity_level_critical_data_warning_title",
+                        "overall_severity_level_critical_passwords_warning_title",
+                        "overall_severity_level_critical_personal_warning_title");
         }
 
         Log.w(TAG, "Unexpected SafetyCenterStatus.OverallSeverityLevel: " + overallSeverityLevel);
@@ -1015,16 +1110,19 @@
 
     @NonNull
     private String getStatusTitleFromIssueCategories(
-            @NonNull List<SafetyCenterIssueWithCategory> safetyCenterIssuesWithCategories,
+            @Nullable SafetyCenterIssueExtended topNonDismissedIssueExtended,
             @NonNull String deviceResourceName,
             @NonNull String accountResourceName,
-            @NonNull String generalResourceName) {
+            @NonNull String generalResourceName,
+            @NonNull String dataResourceName,
+            @NonNull String passwordsResourceName,
+            @NonNull String personalSafetyResourceName) {
         String generalString = mSafetyCenterResourcesContext.getStringByName(generalResourceName);
-        if (safetyCenterIssuesWithCategories.isEmpty()) {
+        if (topNonDismissedIssueExtended == null) {
             Log.w(TAG, "No safety center issues found in a non-green status");
             return generalString;
         }
-        int issueCategory = safetyCenterIssuesWithCategories.get(0).getSafetyCenterIssueCategory();
+        int issueCategory = topNonDismissedIssueExtended.getSafetySourceIssueCategory();
         switch (issueCategory) {
             case SafetySourceIssue.ISSUE_CATEGORY_DEVICE:
                 return mSafetyCenterResourcesContext.getStringByName(deviceResourceName);
@@ -1033,6 +1131,17 @@
             case SafetySourceIssue.ISSUE_CATEGORY_GENERAL:
                 return generalString;
         }
+        if (SdkLevel.isAtLeastU()) {
+            switch (issueCategory) {
+                case SafetySourceIssue.ISSUE_CATEGORY_DATA:
+                    return mSafetyCenterResourcesContext.getStringByName(dataResourceName);
+                case SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS:
+                    return mSafetyCenterResourcesContext.getStringByName(passwordsResourceName);
+                case SafetySourceIssue.ISSUE_CATEGORY_PERSONAL_SAFETY:
+                    return mSafetyCenterResourcesContext.getStringByName(
+                            personalSafetyResourceName);
+            }
+        }
 
         Log.w(TAG, "Unexpected SafetySourceIssue.IssueCategory: " + issueCategory);
         return generalString;
@@ -1125,45 +1234,15 @@
         return SafetySourceKey.of(id.getSafetySourceId(), id.getUserId());
     }
 
-    /** Wrapper that encapsulates both {@link SafetyCenterIssue} and its category. */
-    private static final class SafetyCenterIssueWithCategory {
-        @NonNull private final SafetyCenterIssue mSafetyCenterIssue;
-        @SafetySourceIssue.IssueCategory private final int mSafetyCenterIssueCategory;
-
-        private SafetyCenterIssueWithCategory(
-                @NonNull SafetyCenterIssue safetyCenterIssue,
-                @SafetySourceIssue.IssueCategory int safetyCenterIssueCategory) {
-            this.mSafetyCenterIssue = safetyCenterIssue;
-            this.mSafetyCenterIssueCategory = safetyCenterIssueCategory;
-        }
-
-        @NonNull
-        private SafetyCenterIssue getSafetyCenterIssue() {
-            return mSafetyCenterIssue;
-        }
-
-        @SafetySourceIssue.IssueCategory
-        private int getSafetyCenterIssueCategory() {
-            return mSafetyCenterIssueCategory;
-        }
-
-        private static SafetyCenterIssueWithCategory create(
-                @NonNull SafetyCenterIssue safetyCenterIssue,
-                @SafetySourceIssue.IssueCategory int safetyCenterIssueCategory) {
-            return new SafetyCenterIssueWithCategory(safetyCenterIssue, safetyCenterIssueCategory);
-        }
-    }
-
-    /** A comparator to order {@link SafetyCenterIssueWithCategory} by severity level descending. */
+    /** A comparator to order {@link SafetyCenterIssueExtended} by severity level descending. */
     private static final class SafetyCenterIssuesBySeverityDescending
-            implements Comparator<SafetyCenterIssueWithCategory> {
+            implements Comparator<SafetyCenterIssueExtended> {
 
         private SafetyCenterIssuesBySeverityDescending() {}
 
         @Override
         public int compare(
-                @NonNull SafetyCenterIssueWithCategory left,
-                @NonNull SafetyCenterIssueWithCategory right) {
+                @NonNull SafetyCenterIssueExtended left, @NonNull SafetyCenterIssueExtended right) {
             return Integer.compare(
                     right.getSafetyCenterIssue().getSeverityLevel(),
                     left.getSafetyCenterIssue().getSeverityLevel());
diff --git a/service/java/com/android/safetycenter/SafetyCenterFlags.java b/service/java/com/android/safetycenter/SafetyCenterFlags.java
index e213825..0e508fe 100644
--- a/service/java/com/android/safetycenter/SafetyCenterFlags.java
+++ b/service/java/com/android/safetycenter/SafetyCenterFlags.java
@@ -30,6 +30,8 @@
 
 import androidx.annotation.RequiresApi;
 
+import com.android.modules.utils.build.SdkLevel;
+
 import java.io.PrintWriter;
 import java.time.Duration;
 
@@ -42,6 +44,12 @@
     /** {@link DeviceConfig} property name for {@link #getSafetyCenterEnabled()}. */
     static final String PROPERTY_SAFETY_CENTER_ENABLED = "safety_center_is_enabled";
 
+    private static final String PROPERTY_NOTIFICATIONS_ENABLED =
+            "safety_center_notifications_enabled";
+
+    private static final String PROPERTY_NOTIFICATIONS_ALLOWED_SOURCES =
+            "safety_center_notifications_allowed_sources";
+
     private static final String PROPERTY_SHOW_ERROR_ENTRIES_ON_TIMEOUT =
             "safety_center_show_error_entries_on_timeout";
 
@@ -74,6 +82,11 @@
     private static final String PROPERTY_ALLOW_STATSD_LOGGING_IN_TESTS =
             "safety_center_allow_statsd_logging_in_tests";
 
+    private static final String PROPERTY_SHOW_SUBPAGES = "safety_center_show_subpages";
+
+    private static final String PROPERTY_OVERRIDE_REFRESH_ON_PAGE_OPEN_SOURCES =
+            "safety_center_override_refresh_on_page_open_sources";
+
     private static final Duration REFRESH_SOURCES_TIMEOUT_DEFAULT_DURATION = Duration.ofSeconds(15);
 
     private static final Duration RESOLVING_ACTION_TIMEOUT_DEFAULT_DURATION =
@@ -89,6 +102,8 @@
     static void dump(@NonNull PrintWriter fout) {
         fout.println("FLAGS");
         printFlag(fout, PROPERTY_SAFETY_CENTER_ENABLED, getSafetyCenterEnabled());
+        printFlag(fout, PROPERTY_NOTIFICATIONS_ENABLED, getNotificationsEnabled());
+        printFlag(fout, PROPERTY_NOTIFICATIONS_ALLOWED_SOURCES, getNotificationsAllowedSourceIds());
         printFlag(fout, PROPERTY_SHOW_ERROR_ENTRIES_ON_TIMEOUT, getShowErrorEntriesOnTimeout());
         printFlag(fout, PROPERTY_REPLACE_LOCK_SCREEN_ICON_ACTION, getReplaceLockScreenIconAction());
         printFlag(fout, PROPERTY_RESOLVING_ACTION_TIMEOUT_MILLIS, getResolvingActionTimeout());
@@ -104,6 +119,11 @@
                 fout, PROPERTY_REFRESH_SOURCES_TIMEOUTS_MILLIS, getRefreshSourcesTimeoutsMillis());
         printFlag(fout, PROPERTY_ISSUE_CATEGORY_ALLOWLISTS, getIssueCategoryAllowlists());
         printFlag(fout, PROPERTY_ALLOW_STATSD_LOGGING_IN_TESTS, getAllowStatsdLoggingInTests());
+        printFlag(fout, PROPERTY_SHOW_SUBPAGES, getShowSubpages());
+        printFlag(
+                fout,
+                PROPERTY_OVERRIDE_REFRESH_ON_PAGE_OPEN_SOURCES,
+                getOverrideRefreshOnPageOpenSourceIds());
         fout.println();
     }
 
@@ -120,6 +140,28 @@
         return getBoolean(PROPERTY_SAFETY_CENTER_ENABLED, false);
     }
 
+    /** Returns whether Safety Center notifications are enabled. */
+    static boolean getNotificationsEnabled() {
+        return getBoolean(PROPERTY_NOTIFICATIONS_ENABLED, false);
+    }
+
+    /**
+     * Returns the IDs of sources that Safety Center can send notifications about, in addition to
+     * those permitted by the current XML config.
+     *
+     * <p>If the ID of a source appears on this list then Safety Center may send notifications about
+     * issues from that source, regardless of (overriding) the XML config. If the ID of a source is
+     * absent from this list, then Safety Center may send such notifications only if the XML config
+     * allows it.
+     *
+     * <p>Note that the {@code areNotificationsAllowed} config attribute is only available on API U+
+     * and therefore this is the only way to enable notifications for sources on Android T.
+     */
+    @NonNull
+    static ArraySet<String> getNotificationsAllowedSourceIds() {
+        return getCommaSeparatedStrings(PROPERTY_NOTIFICATIONS_ALLOWED_SOURCES);
+    }
+
     /**
      * Returns whether we should show error entries for sources that timeout when refreshing them.
      */
@@ -284,6 +326,23 @@
         return getBoolean(PROPERTY_ALLOW_STATSD_LOGGING_IN_TESTS, false);
     }
 
+    /**
+     * Returns whether to show subpages in the Safety Center UI for Android-U instead of the
+     * expand-and-collapse list implementation.
+     */
+    static boolean getShowSubpages() {
+        // TODO(b/260822348): Add CTS test to verify that the flag is disabled when turned on for T
+        return SdkLevel.isAtLeastU() && getBoolean(PROPERTY_SHOW_SUBPAGES, true);
+    }
+
+    /**
+     * Returns an array of safety source Ids that will be refreshed on page open, even if
+     * refreshOnPageOpenAllowed is false (the default) in the XML config.
+     */
+    static ArraySet<String> getOverrideRefreshOnPageOpenSourceIds() {
+        return getCommaSeparatedStrings(PROPERTY_OVERRIDE_REFRESH_ON_PAGE_OPEN_SOURCES);
+    }
+
     @NonNull
     private static Duration getDuration(@NonNull String property, @NonNull Duration defaultValue) {
         return Duration.ofMillis(getLong(property, defaultValue.toMillis()));
diff --git a/service/java/com/android/safetycenter/SafetyCenterIssueCache.java b/service/java/com/android/safetycenter/SafetyCenterIssueCache.java
index 88d6a87..ed3d997 100644
--- a/service/java/com/android/safetycenter/SafetyCenterIssueCache.java
+++ b/service/java/com/android/safetycenter/SafetyCenterIssueCache.java
@@ -69,14 +69,19 @@
     }
 
     /**
-     * Counts the total number of issues in the issue cache, from currently-active sources, in the
-     * given {@link UserProfileGroup}.
+     * Counts the total number of issues in the issue cache, from currently-active, loggable
+     * sources, in the given {@link UserProfileGroup}.
      */
-    int countActiveIssues(@NonNull UserProfileGroup userProfileGroup) {
+    int countActiveLoggableIssues(@NonNull UserProfileGroup userProfileGroup) {
         int issueCount = 0;
         for (int i = 0; i < mIssues.size(); i++) {
             SafetyCenterIssueKey issueKey = mIssues.keyAt(i);
-            if (mSafetyCenterConfigReader.isExternalSafetySourceActive(issueKey.getSafetySourceId())
+            String safetySourceId = issueKey.getSafetySourceId();
+            SafetyCenterConfigReader.ExternalSafetySource externalSafetySource =
+                    mSafetyCenterConfigReader.getExternalSafetySource(safetySourceId);
+            if (mSafetyCenterConfigReader.isExternalSafetySourceActive(safetySourceId)
+                    && externalSafetySource != null
+                    && SafetySources.isLoggable(externalSafetySource.getSafetySource())
                     && userProfileGroup.contains(issueKey.getUserId())) {
                 issueCount++;
             }
@@ -85,6 +90,35 @@
     }
 
     /**
+     * Gets all the issues in the issue cache for the given {@code userId}.
+     *
+     * <p>Only issues from "active" sources are included. Active sources are those for which {@link
+     * SafetyCenterConfigReader#isExternalSafetySourceActive(String)} returns {@code true}.
+     */
+    @NonNull
+    List<SafetyCenterIssueKey> getIssuesForUser(@UserIdInt int userId) {
+        ArrayList<SafetyCenterIssueKey> result = new ArrayList<>();
+        for (int i = 0; i < mIssues.size(); i++) {
+            SafetyCenterIssueKey issueKey = mIssues.keyAt(i);
+            if (issueKey.getUserId() != userId) {
+                continue;
+            }
+            String safetySourceId = issueKey.getSafetySourceId();
+            if (mSafetyCenterConfigReader.isExternalSafetySourceActive(safetySourceId)) {
+                result.add(issueKey);
+            }
+        }
+        return result;
+    }
+
+    /** Same as {@link SafetyCenterIssueCache#isIssueDismissed(SafetyCenterIssueKey, int)}. */
+    boolean isIssueDismissed(@NonNull SafetyCenterIssueExtended safetyCenterIssue) {
+        return isIssueDismissed(
+                safetyCenterIssue.getSafetyCenterIssueKey(),
+                safetyCenterIssue.getSafetySourceIssueSeverityLevel());
+    }
+
+    /**
      * Returns {@code true} if the issue with the given key and severity level is currently
      * dismissed.
      *
@@ -126,7 +160,9 @@
     }
 
     /**
-     * Dismisses the issue with the given key.
+     * Marks the issue with the given key as dismissed.
+     *
+     * <p>That issue's notification (if any) is also marked as dismissed.
      *
      * <p>This method may change the value reported by {@link #isDirty} to {@code true}.
      */
@@ -135,12 +171,77 @@
         if (issueData == null) {
             return;
         }
-        issueData.setDismissedAt(Instant.now());
+        Instant now = Instant.now();
+        issueData.setDismissedAt(now);
         issueData.setDismissCount(issueData.getDismissCount() + 1);
+        issueData.setNotificationDismissedAt(now);
         mIsDirty = true;
     }
 
     /**
+     * Copy dismissal data from one issue to the other.
+     *
+     * <p>This will align dismissal state of these issues, unless issues are of different
+     * severities, in which case they can potentially differ in resurface times.
+     */
+    void copyDismissalData(
+            @NonNull SafetyCenterIssueKey keyFrom, @NonNull SafetyCenterIssueKey keyTo) {
+        IssueData dataFrom = getOrWarn(keyFrom, "copying dismissed data");
+        IssueData dataTo = getOrWarn(keyTo, "copying dismissed data");
+        if (dataFrom == null || dataTo == null) {
+            return;
+        }
+
+        dataTo.setDismissedAt(dataFrom.getDismissedAt());
+        dataTo.setDismissCount(dataFrom.getDismissCount());
+        mIsDirty = true;
+    }
+
+    /**
+     * Marks the notification (if any) of the issue with the given key as dismissed.
+     *
+     * <p>The issue itself is <strong>not</strong> marked as dismissed and its warning card can
+     * still appear in the Safety Center UI.
+     *
+     * <p>This method may change the value reported by {@link #isDirty} to {@code true}.
+     */
+    void dismissNotification(@NonNull SafetyCenterIssueKey safetyCenterIssueKey) {
+        IssueData issueData = getOrWarn(safetyCenterIssueKey, "dismissing notification");
+        if (issueData == null) {
+            return;
+        }
+        issueData.setNotificationDismissedAt(Instant.now());
+        mIsDirty = true;
+    }
+
+    /**
+     * Returns the {@link Instant} when the issue with the given key was first reported to Safety
+     * Center.
+     */
+    @Nullable
+    Instant getIssueFirstSeenAt(@NonNull SafetyCenterIssueKey safetyCenterIssueKey) {
+        IssueData issueData = getOrWarn(safetyCenterIssueKey, "getting first seen");
+        if (issueData == null) {
+            return null;
+        }
+        return issueData.getFirstSeenAt();
+    }
+
+    /**
+     * Returns the {@link Instant} when the notification for the issue with the given key was last
+     * dismissed.
+     */
+    // TODO(b/261429824): Handle mNotificationDismissedAt w.r.t. issue deduplication
+    @Nullable
+    Instant getNotificationDismissedAt(@NonNull SafetyCenterIssueKey safetyCenterIssueKey) {
+        IssueData issueData = getOrWarn(safetyCenterIssueKey, "getting notification dismissed");
+        if (issueData == null) {
+            return null;
+        }
+        return issueData.getNotificationDismissedAt();
+    }
+
+    /**
      * Updates the issue cache to contain exactly the given {@code safetySourceIssueIds} for the
      * supplied source and user.
      */
@@ -295,6 +396,7 @@
             IssueData issueData = new IssueData(persistedIssue.getFirstSeenAt());
             issueData.setDismissedAt(persistedIssue.getDismissedAt());
             issueData.setDismissCount(persistedIssue.getDismissCount());
+            // TODO(b/259083534): Persist notification dismissal state across reboots
             return issueData;
         }
 
@@ -303,6 +405,8 @@
         @Nullable private Instant mDismissedAt;
         private int mDismissCount;
 
+        @Nullable private Instant mNotificationDismissedAt;
+
         private IssueData(@NonNull Instant firstSeenAt) {
             mFirstSeenAt = firstSeenAt;
         }
@@ -329,12 +433,22 @@
             mDismissCount = dismissCount;
         }
 
+        @Nullable
+        private Instant getNotificationDismissedAt() {
+            return mNotificationDismissedAt;
+        }
+
+        private void setNotificationDismissedAt(@Nullable Instant notificationDismissedAt) {
+            mNotificationDismissedAt = notificationDismissedAt;
+        }
+
         @NonNull
         private PersistedSafetyCenterIssue.Builder toPersistedIssueBuilder() {
             return new PersistedSafetyCenterIssue.Builder()
                     .setFirstSeenAt(mFirstSeenAt)
                     .setDismissedAt(mDismissedAt)
                     .setDismissCount(mDismissCount);
+            // TODO(b/259083534): Persist notification dismissal state across reboots
         }
 
         @Override
@@ -346,6 +460,8 @@
                     + mDismissedAt
                     + ", mDismissCount="
                     + mDismissCount
+                    + ", mNotificationDismissedAt="
+                    + mNotificationDismissedAt
                     + '}';
         }
     }
diff --git a/service/java/com/android/safetycenter/SafetyCenterIssueDeduplicator.java b/service/java/com/android/safetycenter/SafetyCenterIssueDeduplicator.java
new file mode 100644
index 0000000..50a8f0e
--- /dev/null
+++ b/service/java/com/android/safetycenter/SafetyCenterIssueDeduplicator.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.safetycenter;
+
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+
+import android.annotation.NonNull;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+
+import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+import javax.annotation.concurrent.NotThreadSafe;
+
+/** Deduplicates issues based on deduplication info provided by the source and the issue. */
+@RequiresApi(UPSIDE_DOWN_CAKE)
+@NotThreadSafe
+final class SafetyCenterIssueDeduplicator {
+
+    @NonNull private final SafetyCenterIssueCache mSafetyCenterIssueCache;
+
+    SafetyCenterIssueDeduplicator(@NonNull SafetyCenterIssueCache safetyCenterIssueCache) {
+        this.mSafetyCenterIssueCache = safetyCenterIssueCache;
+    }
+
+    /**
+     * Accepts a list of issues sorted by priority and filters out duplicates.
+     *
+     * <p>Issues are considered duplicate if they have the same deduplication id and were sent by
+     * sources which are part of the same deduplication group. All but the highest priority
+     * duplicate issue will be filtered out.
+     *
+     * <p>In case any issue, in the bucket of duplicate issues, was dismissed, all issues of the
+     * same or lower severity will be dismissed as well.
+     *
+     * <p>This method modifies the given argument.
+     */
+    void deduplicateIssues(@NonNull List<SafetyCenterIssueExtended> sortedIssues) {
+        // (dedup key) -> list(issues)
+        ArrayMap<DeduplicationKey, List<SafetyCenterIssueExtended>> dedupBuckets =
+                createDedupBuckets(sortedIssues);
+
+        dismissDuplicateIssuesOfDismissedIssue(dedupBuckets);
+
+        ArraySet<SafetyCenterIssueKey> duplicatesToFilterOut =
+                getDuplicatesToFilterOut(dedupBuckets);
+
+        Iterator<SafetyCenterIssueExtended> it = sortedIssues.iterator();
+        while (it.hasNext()) {
+            if (duplicatesToFilterOut.contains(it.next().getSafetyCenterIssueKey())) {
+                it.remove();
+            }
+        }
+    }
+
+    /**
+     * Handles dismissals logic: in each bucket, dismissal details of the top (highest priority)
+     * dismissed issue will be copied to all other duplicate issues in that bucket, that are of
+     * equal or lower severity (not priority).
+     */
+    private void dismissDuplicateIssuesOfDismissedIssue(
+            @NonNull ArrayMap<DeduplicationKey, List<SafetyCenterIssueExtended>> dedupBuckets) {
+        for (int i = 0; i < dedupBuckets.size(); i++) {
+            List<SafetyCenterIssueExtended> duplicates = dedupBuckets.valueAt(i);
+            SafetyCenterIssueExtended topDismissed = getHighestPriorityDismissedIssue(duplicates);
+            alignDismissalsDataWithinBucket(topDismissed, duplicates);
+        }
+    }
+
+    /**
+     * Dismisses all issues of lower or equal severity relative to the given top dismissed issue in
+     * the bucket.
+     */
+    private void alignDismissalsDataWithinBucket(
+            @Nullable SafetyCenterIssueExtended topDismissed,
+            @NonNull List<SafetyCenterIssueExtended> duplicates) {
+        if (topDismissed == null) {
+            return;
+        }
+        SafetyCenterIssueKey topDismissedIssueKey = topDismissed.getSafetyCenterIssueKey();
+        int topDismissedSeverityLevel = topDismissed.getSafetyCenterIssue().getSeverityLevel();
+        for (int i = 0; i < duplicates.size(); i++) {
+            SafetyCenterIssueExtended issue = duplicates.get(i);
+            SafetyCenterIssueKey issueKey = issue.getSafetyCenterIssueKey();
+            if (!issueKey.equals(topDismissedIssueKey)
+                    && issue.getSafetyCenterIssue().getSeverityLevel()
+                            <= topDismissedSeverityLevel) {
+                // all duplicate issues should have same dismissals data as top dismissed issue
+                mSafetyCenterIssueCache.copyDismissalData(topDismissedIssueKey, issueKey);
+            }
+        }
+    }
+
+    @Nullable
+    private SafetyCenterIssueExtended getHighestPriorityDismissedIssue(
+            @NonNull List<SafetyCenterIssueExtended> duplicates) {
+        for (int i = 0; i < duplicates.size(); i++) {
+            SafetyCenterIssueExtended issue = duplicates.get(i);
+            if (mSafetyCenterIssueCache.isIssueDismissed(issue)) {
+                return issue;
+            }
+        }
+
+        return null;
+    }
+
+    /** Returns a set of duplicate issues that need to be filtered out. */
+    @NonNull
+    private static ArraySet<SafetyCenterIssueKey> getDuplicatesToFilterOut(
+            @NonNull ArrayMap<DeduplicationKey, List<SafetyCenterIssueExtended>> dedupBuckets) {
+        ArraySet<SafetyCenterIssueKey> duplicatesToFilterOut = new ArraySet<>();
+
+        for (int i = 0; i < dedupBuckets.size(); i++) {
+            List<SafetyCenterIssueExtended> duplicates = dedupBuckets.valueAt(i);
+            // all but the top one in the bucket
+            for (int j = 1; j < duplicates.size(); j++) {
+                duplicatesToFilterOut.add(duplicates.get(j).getSafetyCenterIssueKey());
+            }
+        }
+
+        return duplicatesToFilterOut;
+    }
+
+    /** Returns a mapping (dedup key) -> list(issues). */
+    @NonNull
+    private static ArrayMap<DeduplicationKey, List<SafetyCenterIssueExtended>> createDedupBuckets(
+            @NonNull List<SafetyCenterIssueExtended> sortedIssues) {
+        ArrayMap<DeduplicationKey, List<SafetyCenterIssueExtended>> dedupBuckets = new ArrayMap<>();
+
+        for (int i = 0; i < sortedIssues.size(); i++) {
+            SafetyCenterIssueExtended issue = sortedIssues.get(i);
+            DeduplicationKey dedupKey = getDedupKey(issue);
+            if (dedupKey == null) {
+                continue;
+            }
+
+            // each bucket will remain sorted
+            List<SafetyCenterIssueExtended> bucket =
+                    dedupBuckets.getOrDefault(dedupKey, new ArrayList<>());
+            bucket.add(issue);
+
+            dedupBuckets.put(dedupKey, bucket);
+        }
+
+        return dedupBuckets;
+    }
+
+    /** Returns deduplication key of the given issue. */
+    @Nullable
+    private static DeduplicationKey getDedupKey(@NonNull SafetyCenterIssueExtended issue) {
+        if (issue.getDeduplicationGroup() == null || issue.getDeduplicationId() == null) {
+            return null;
+        }
+        return new DeduplicationKey(issue.getDeduplicationGroup(), issue.getDeduplicationId());
+    }
+
+    private static class DeduplicationKey {
+
+        @NonNull private final String mDeduplicationGroup;
+        @NonNull private final String mDeduplicationId;
+
+        private DeduplicationKey(
+                @NonNull String deduplicationGroup, @NonNull String deduplicationId) {
+            mDeduplicationGroup = deduplicationGroup;
+            mDeduplicationId = deduplicationId;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mDeduplicationGroup, mDeduplicationId);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof DeduplicationKey)) return false;
+            DeduplicationKey dedupKey = (DeduplicationKey) o;
+            return mDeduplicationGroup.equals(dedupKey.mDeduplicationGroup)
+                    && mDeduplicationId.equals(dedupKey.mDeduplicationId);
+        }
+    }
+}
diff --git a/service/java/com/android/safetycenter/SafetyCenterIssueExtended.java b/service/java/com/android/safetycenter/SafetyCenterIssueExtended.java
new file mode 100644
index 0000000..60e0b5b
--- /dev/null
+++ b/service/java/com/android/safetycenter/SafetyCenterIssueExtended.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.safetycenter;
+
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.safetycenter.SafetyCenterIssue;
+import android.safetycenter.SafetySourceData;
+import android.safetycenter.SafetySourceIssue;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.safetycenter.internaldata.SafetyCenterIds;
+import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
+
+import javax.annotation.concurrent.NotThreadSafe;
+
+/** Wrapper that contains a {@link SafetyCenterIssue} and some extra information about it. */
+@RequiresApi(TIRAMISU)
+final class SafetyCenterIssueExtended {
+    @NonNull private final SafetyCenterIssue mSafetyCenterIssue;
+    @NonNull private final SafetyCenterIssueKey mSafetyCenterIssueKey;
+    @SafetySourceIssue.IssueCategory private final int mSafetySourceIssueCategory;
+    @SafetySourceData.SeverityLevel private final int mSafetySourceIssueSeverityLevel;
+
+    // Deduplication info, only available on Android U+.
+    @Nullable private final String mDeduplicationGroup;
+    @Nullable private final String mDeduplicationId;
+
+    private SafetyCenterIssueExtended(
+            @NonNull SafetyCenterIssue safetyCenterIssue,
+            @NonNull SafetyCenterIssueKey safetyCenterIssueKey,
+            @SafetySourceIssue.IssueCategory int safetySourceIssueCategory,
+            int safetySourceIssueSeverityLevel,
+            @Nullable String deduplicationGroup,
+            @Nullable String deduplicationId) {
+        this.mSafetyCenterIssue = safetyCenterIssue;
+        this.mSafetyCenterIssueKey = safetyCenterIssueKey;
+        this.mSafetySourceIssueCategory = safetySourceIssueCategory;
+        this.mSafetySourceIssueSeverityLevel = safetySourceIssueSeverityLevel;
+        this.mDeduplicationGroup = deduplicationGroup;
+        this.mDeduplicationId = deduplicationId;
+    }
+
+    /** Returns the {@link SafetyCenterIssue} it contains. */
+    @NonNull
+    SafetyCenterIssue getSafetyCenterIssue() {
+        return mSafetyCenterIssue;
+    }
+
+    /** Returns the {@link SafetyCenterIssueKey} related to this issue. */
+    @NonNull
+    SafetyCenterIssueKey getSafetyCenterIssueKey() {
+        return mSafetyCenterIssueKey;
+    }
+
+    /** Returns the {@link SafetySourceIssue.IssueCategory} related to this issue. */
+    @SafetySourceIssue.IssueCategory
+    int getSafetySourceIssueCategory() {
+        return mSafetySourceIssueCategory;
+    }
+
+    /** Returns the {@link SafetySourceData.SeverityLevel} related to this issue. */
+    @SafetySourceData.SeverityLevel
+    int getSafetySourceIssueSeverityLevel() {
+        return mSafetySourceIssueSeverityLevel;
+    }
+
+    /** Returns the deduplication group related to this issue. */
+    @Nullable
+    String getDeduplicationGroup() {
+        return mDeduplicationGroup;
+    }
+
+    /** Returns the deduplication id related to this issue. */
+    @Nullable
+    String getDeduplicationId() {
+        return mDeduplicationId;
+    }
+
+    /** Builder for the {@link SafetyCenterIssueExtended}. */
+    @NotThreadSafe
+    static final class Builder {
+        @NonNull private final SafetyCenterIssue mSafetyCenterIssue;
+        @SafetySourceIssue.IssueCategory private final int mSafetySourceIssueCategory;
+        @SafetySourceData.SeverityLevel private final int mSafetySourceIssueSeverityLevel;
+
+        @Nullable private String mDeduplicationGroup;
+        @Nullable private String mDeduplicationId;
+
+        /** Constructs a new instance of the builder. */
+        Builder(
+                @NonNull SafetyCenterIssue safetyCenterIssue,
+                @SafetySourceIssue.IssueCategory int safetySourceIssueCategory,
+                @SafetySourceData.SeverityLevel int safetySourceIssueSeverityLevel) {
+            this.mSafetyCenterIssue = safetyCenterIssue;
+            this.mSafetySourceIssueCategory = safetySourceIssueCategory;
+            this.mSafetySourceIssueSeverityLevel = safetySourceIssueSeverityLevel;
+        }
+
+        /** Sets the deduplication group for this issue. */
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        Builder setDeduplicationGroup(@Nullable String deduplicationGroup) {
+            this.mDeduplicationGroup = deduplicationGroup;
+            return this;
+        }
+
+        /** Sets the deduplication id for this issue. */
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        Builder setDeduplicationId(@Nullable String deduplicationId) {
+            this.mDeduplicationId = deduplicationId;
+            return this;
+        }
+
+        /** Returns a new {@link SafetyCenterIssueExtended} based on previously given data. */
+        SafetyCenterIssueExtended build() {
+            return new SafetyCenterIssueExtended(
+                    mSafetyCenterIssue,
+                    SafetyCenterIds.issueIdFromString(mSafetyCenterIssue.getId())
+                            .getSafetyCenterIssueKey(),
+                    mSafetySourceIssueCategory,
+                    mSafetySourceIssueSeverityLevel,
+                    mDeduplicationGroup,
+                    mDeduplicationId);
+        }
+    }
+}
diff --git a/service/java/com/android/safetycenter/SafetyCenterListeners.java b/service/java/com/android/safetycenter/SafetyCenterListeners.java
index f7abdc1..f911f01 100644
--- a/service/java/com/android/safetycenter/SafetyCenterListeners.java
+++ b/service/java/com/android/safetycenter/SafetyCenterListeners.java
@@ -142,10 +142,11 @@
      * Adds a {@link IOnSafetyCenterDataChangedListener} for the given {@code packageName} and
      * {@code userId}.
      *
-     * <p>Returns whether the callback was successfully registered. Returns {@code true} if the
-     * callback was already registered.
+     * <p>Returns the registered {@link IOnSafetyCenterDataChangedListener} if this operation was
+     * successful. Otherwise, returns {@code null}.
      */
-    boolean addListener(
+    @Nullable
+    IOnSafetyCenterDataChangedListener addListener(
             @NonNull IOnSafetyCenterDataChangedListener listener,
             @NonNull String packageName,
             @UserIdInt int userId) {
@@ -153,11 +154,15 @@
                 mSafetyCenterDataChangedListeners.get(userId);
         if (listeners == null) {
             listeners = new RemoteCallbackList<>();
-            mSafetyCenterDataChangedListeners.put(userId, listeners);
         }
         OnSafetyCenterDataChangedListenerWrapper listenerWrapper =
                 new OnSafetyCenterDataChangedListenerWrapper(listener, packageName);
-        return listeners.register(listenerWrapper);
+        boolean registered = listeners.register(listenerWrapper);
+        if (!registered) {
+            return null;
+        }
+        mSafetyCenterDataChangedListeners.put(userId, listeners);
+        return listenerWrapper;
     }
 
     /**
@@ -229,7 +234,7 @@
                     safetyCenterData = cachedSafetyCenterData;
                 } else {
                     safetyCenterData =
-                            mSafetyCenterDataFactory.getSafetyCenterData(
+                            mSafetyCenterDataFactory.assembleSafetyCenterData(
                                     packageName, userProfileGroup);
                     safetyCenterDataCache.put(packageName, safetyCenterData);
                 }
diff --git a/service/java/com/android/safetycenter/SafetyCenterNotificationFactory.java b/service/java/com/android/safetycenter/SafetyCenterNotificationFactory.java
new file mode 100644
index 0000000..60acda0
--- /dev/null
+++ b/service/java/com/android/safetycenter/SafetyCenterNotificationFactory.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.safetycenter;
+
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.safetycenter.SafetySourceIssue;
+import android.util.Log;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
+
+/**
+ * Factory that builds {@link Notification} objects from {@link SafetySourceIssue} instances with
+ * appropriate {@link PendingIntent}s for click and dismiss callbacks.
+ */
+@RequiresApi(TIRAMISU)
+final class SafetyCenterNotificationFactory {
+
+    private static final String TAG = "SafetyCenterNF";
+
+    private static final int OPEN_SAFETY_CENTER_REQUEST_CODE = 1221;
+
+    @NonNull private final Context mContext;
+
+    SafetyCenterNotificationFactory(@NonNull Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Creates and returns a new {@link Notification} instance corresponding to the given issue, or
+     * {@code null} if none could be created.
+     *
+     * <p>The provided {@link NotificationManager} is used to create or update the {@link
+     * NotificationChannel} for the notification.
+     */
+    @Nullable
+    Notification newNotificationForIssue(
+            @NonNull NotificationManager notificationManager,
+            @NonNull SafetySourceIssue issue,
+            @NonNull SafetyCenterIssueKey issueKey) {
+        String channelId = createAndGetChannelId(notificationManager, issue);
+
+        if (channelId == null) {
+            return null;
+        }
+
+        Notification.Builder builder =
+                new Notification.Builder(mContext, channelId)
+                        // TODO(b/259399024): Use suitable icon here
+                        .setSmallIcon(android.R.drawable.ic_safety_protection)
+                        .setExtras(getNotificationExtras())
+                        .setContentTitle(issue.getTitle())
+                        .setContentText(issue.getSummary())
+                        .setContentIntent(newSafetyCenterPendingIntent(issue))
+                        .setDeleteIntent(
+                                SafetyCenterNotificationReceiver.newNotificationDismissedIntent(
+                                        mContext, issueKey));
+        // TODO(b/260084296): Include issue actions on notifications;
+
+        return builder.build();
+    }
+
+    @Nullable
+    private String createAndGetChannelId(
+            @NonNull NotificationManager notificationManager, @NonNull SafetySourceIssue issue) {
+        // TODO(b/259398016): Different channels for different issues/severities
+        NotificationChannel channel =
+                new NotificationChannel(
+                        "safety_center",
+                        // TODO(b/259399024): Use suitable string here
+                        "Safety Center notifications",
+                        NotificationManager.IMPORTANCE_DEFAULT);
+        return createNotificationChannelWithoutCallingIdentity(notificationManager, channel);
+    }
+
+    @NonNull
+    private PendingIntent newSafetyCenterPendingIntent(@NonNull SafetySourceIssue targetIssue) {
+        // TODO(b/259398419): Add target issue to intent so it's highlighted when SC opens
+        Intent intent = new Intent(Intent.ACTION_SAFETY_CENTER);
+        return PendingIntentFactory.getActivityPendingIntent(
+                mContext, OPEN_SAFETY_CENTER_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE);
+    }
+
+    @NonNull
+    private Bundle getNotificationExtras() {
+        Bundle extras = new Bundle();
+        // TODO(b/259399024): Use suitable string resource here
+        extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, "Safety Center");
+        return extras;
+    }
+
+    /**
+     * Creates a {@link NotificationChannel} using the given {@link NotificationManager}, dropping
+     * any calling identity so that it can be unblockable. Returns the new channel's ID if it was
+     * created successfully or {@code null} otherwise.
+     */
+    @Nullable
+    private static String createNotificationChannelWithoutCallingIdentity(
+            @NonNull NotificationManager notificationManager,
+            @NonNull NotificationChannel channel) {
+        // Clearing calling identity to be able to make an unblockable system notification channel
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            notificationManager.createNotificationChannel(channel);
+            return channel.getId();
+        } catch (RuntimeException e) {
+            Log.w(TAG, "Unable to create notification channel", e);
+            return null;
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+}
diff --git a/service/java/com/android/safetycenter/SafetyCenterNotificationReceiver.java b/service/java/com/android/safetycenter/SafetyCenterNotificationReceiver.java
new file mode 100644
index 0000000..2ae5803
--- /dev/null
+++ b/service/java/com/android/safetycenter/SafetyCenterNotificationReceiver.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.safetycenter;
+
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.safetycenter.internaldata.SafetyCenterIds;
+import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
+
+/**
+ * A Context-registered {@link BroadcastReceiver} that handles intents sent via Safety Center
+ * notifications e.g. when a notification is dismissed.
+ *
+ * <p>Use {@link #register(Context)} to register this receiver with the correct {@link IntentFilter}
+ * and use the {@link #newNotificationDismissedIntent(Context, SafetyCenterIssueKey)} factory method
+ * to create new intents for this receiver.
+ */
+@RequiresApi(TIRAMISU)
+final class SafetyCenterNotificationReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "SafetyCenterNR";
+
+    private static final String ACTION_NOTIFICATION_DISMISSED =
+            "com.android.safetycenter.action.NOTIFICATION_DISMISSED";
+    private static final String EXTRA_ISSUE_KEY = "com.android.safetycenter.extra.ISSUE_KEY";
+
+    private static final int REQUEST_CODE_UNUSED = 0;
+
+    /**
+     * Creates a {@code PendingIntent} for a broadcast {@code Intent} which will start this receiver
+     * and cause it to handle a notification dismissal event.
+     */
+    @NonNull
+    static PendingIntent newNotificationDismissedIntent(
+            @NonNull Context context, @NonNull SafetyCenterIssueKey issueKey) {
+        String issueKeyString = SafetyCenterIds.encodeToString(issueKey);
+        Intent intent = new Intent(ACTION_NOTIFICATION_DISMISSED);
+        intent.putExtra(EXTRA_ISSUE_KEY, issueKeyString);
+        intent.setIdentifier(issueKeyString);
+        int flags = PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT;
+        return PendingIntentFactory.getNonProtectedSystemOnlyBroadcastPendingIntent(
+                context, REQUEST_CODE_UNUSED, intent, flags);
+    }
+
+    @Nullable
+    private static SafetyCenterIssueKey getIssueKeyExtra(@NonNull Intent intent) {
+        String issueKeyString = intent.getStringExtra(EXTRA_ISSUE_KEY);
+        if (issueKeyString == null) {
+            Log.w(TAG, "Received notification dismissed broadcast with null issue key extra");
+            return null;
+        }
+        try {
+            return SafetyCenterIds.issueKeyFromString(issueKeyString);
+        } catch (IllegalArgumentException e) {
+            Log.w(TAG, "Could not decode the issue key extra", e);
+            return null;
+        }
+    }
+
+    @NonNull private final SafetyCenterIssueCache mIssueCache;
+    @NonNull private final Object mApiLock;
+
+    SafetyCenterNotificationReceiver(
+            @NonNull SafetyCenterIssueCache issueCache, @NonNull Object apiLock) {
+        mIssueCache = issueCache;
+        mApiLock = apiLock;
+    }
+
+    /**
+     * Register this receiver in the given {@link Context} with an {@link IntentFilter} that matches
+     * any intents created by this class' static factory methods.
+     *
+     * @see #newNotificationDismissedIntent(Context, SafetyCenterIssueKey)
+     */
+    void register(@NonNull Context context) {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_NOTIFICATION_DISMISSED);
+        context.registerReceiver(this, filter, Context.RECEIVER_NOT_EXPORTED);
+    }
+
+    @Override
+    public void onReceive(@NonNull Context context, @NonNull Intent intent) {
+        if (!SafetyCenterFlags.getSafetyCenterEnabled()
+                || !SafetyCenterFlags.getNotificationsEnabled()) {
+            return;
+        }
+
+        Log.d(TAG, "Received broadcast with action " + intent.getAction());
+        String action = intent.getAction();
+        if (action == null) {
+            Log.w(TAG, "Received broadcast with null action!");
+            return;
+        }
+
+        if (ACTION_NOTIFICATION_DISMISSED.equals(action)) {
+            onNotificationDismissed(intent);
+        } else {
+            Log.w(TAG, "Received broadcast with unrecognized action: " + action);
+        }
+    }
+
+    private void onNotificationDismissed(@NonNull Intent intent) {
+        SafetyCenterIssueKey issueKey = getIssueKeyExtra(intent);
+        if (issueKey == null) {
+            return;
+        }
+        synchronized (mApiLock) {
+            mIssueCache.dismissNotification(issueKey);
+        }
+    }
+}
diff --git a/service/java/com/android/safetycenter/SafetyCenterNotificationSender.java b/service/java/com/android/safetycenter/SafetyCenterNotificationSender.java
new file mode 100644
index 0000000..0910750
--- /dev/null
+++ b/service/java/com/android/safetycenter/SafetyCenterNotificationSender.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.safetycenter;
+
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import static com.android.safetycenter.internaldata.SafetyCenterIds.toUserFriendlyString;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.safetycenter.SafetySourceIssue;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.modules.utils.build.SdkLevel;
+import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+import java.util.List;
+
+import javax.annotation.concurrent.NotThreadSafe;
+
+/**
+ * Class responsible for posting, updating and dismissing Safety Center notifications each time
+ * Safety Center's issues change.
+ *
+ * <p>This class isn't thread safe. Thread safety must be handled by the caller.
+ */
+@RequiresApi(TIRAMISU)
+@NotThreadSafe
+final class SafetyCenterNotificationSender {
+
+    private static final String TAG = "SafetyCenterNS";
+
+    // We use a fixed notification ID because notifications are keyed by (tag, id) and it easier
+    // to differentiate our notifications using the tag
+    private static final int FIXED_NOTIFICATION_ID = 2345;
+
+    private static final int NOTIFICATION_BEHAVIOR_INTERNAL_NEVER = 100;
+    private static final int NOTIFICATION_BEHAVIOR_INTERNAL_DELAYED = 200;
+    private static final int NOTIFICATION_BEHAVIOR_INTERNAL_IMMEDIATELY = 300;
+
+    /**
+     * Internal notification behavior {@code @IntDef} which is related to the {@code
+     * SafetySourceIssue.NotificationBehavior} type introduced in Android U.
+     *
+     * <p>This definition is available on T+.
+     *
+     * <p>Unlike the U+/external {@code @IntDef}, this one has no "unspecified behavior" value. Any
+     * issues which have unspecified behavior are resolved to one of these specific behaviors based
+     * on their other properties.
+     */
+    @IntDef(
+            prefix = {"NOTIFICATION_BEHAVIOR_INTERNAL"},
+            value = {
+                NOTIFICATION_BEHAVIOR_INTERNAL_NEVER,
+                NOTIFICATION_BEHAVIOR_INTERNAL_DELAYED,
+                NOTIFICATION_BEHAVIOR_INTERNAL_IMMEDIATELY
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface NotificationBehaviorInternal {}
+
+    @NonNull private final Context mContext;
+
+    @NonNull private final SafetyCenterNotificationFactory mNotificationFactory;
+
+    @NonNull private final SafetyCenterIssueCache mIssueCache;
+
+    @NonNull private final SafetyCenterRepository mSafetyCenterRepository;
+
+    @NonNull private final SafetyCenterConfigReader mSafetyCenterConfigReader;
+
+    private final ArrayMap<SafetyCenterIssueKey, SafetySourceIssue> mNotifiedIssues =
+            new ArrayMap<>();
+
+    SafetyCenterNotificationSender(
+            @NonNull Context context,
+            @NonNull SafetyCenterNotificationFactory notificationFactory,
+            @NonNull SafetyCenterIssueCache issueCache,
+            @NonNull SafetyCenterRepository safetyCenterRepository,
+            @NonNull SafetyCenterConfigReader safetyCenterConfigReader) {
+        mContext = context;
+        mNotificationFactory = notificationFactory;
+        mIssueCache = issueCache;
+        mSafetyCenterRepository = safetyCenterRepository;
+        mSafetyCenterConfigReader = safetyCenterConfigReader;
+    }
+
+    /**
+     * Updates Safety Center notifications, usually in response to a change in the issues for the
+     * given userId.
+     */
+    void updateNotifications(@UserIdInt int userId) {
+        if (!SafetyCenterFlags.getNotificationsEnabled()) {
+            return;
+        }
+
+        NotificationManager notificationManager = getNotificationManagerForUser(userId);
+
+        if (notificationManager == null) {
+            Log.w(TAG, "Could not retrieve NotificationManager for user " + userId);
+            return;
+        }
+
+        ArrayMap<SafetyCenterIssueKey, SafetySourceIssue> issuesToNotify =
+                getIssuesToNotify(userId);
+
+        // Post or update notifications for notifiable issues, depending on their behavior,
+        // keeping track of which issues notifications were posted/updated for:
+        ArraySet<SafetyCenterIssueKey> freshIssueKeys = new ArraySet<>();
+        for (int i = 0; i < issuesToNotify.size(); i++) {
+            SafetyCenterIssueKey issueKey = issuesToNotify.keyAt(i);
+            SafetySourceIssue issue = issuesToNotify.valueAt(i);
+
+            boolean unchanged = issue.equals(mNotifiedIssues.get(issueKey));
+            if (unchanged) {
+                freshIssueKeys.add(issueKey);
+                continue;
+            }
+
+            boolean wasPosted = postNotificationForIssue(notificationManager, issue, issueKey);
+            if (wasPosted) {
+                freshIssueKeys.add(issueKey);
+            }
+        }
+
+        // Cancel previously-posted notifications, for this user, which were not just updated:
+        cancelStaleNotifications(notificationManager, userId, freshIssueKeys);
+    }
+
+    /** Cancels all notifications previously posted by this class */
+    void cancelAllNotifications() {
+        // Loop in reverse index order to be able to remove entries while iterating
+        for (int i = mNotifiedIssues.size() - 1; i >= 0; i--) {
+            SafetyCenterIssueKey issueKey = mNotifiedIssues.keyAt(i);
+            cancelNotificationFromSystem(
+                    getNotificationManagerForUser(issueKey.getUserId()),
+                    getNotificationTag(issueKey));
+            mNotifiedIssues.removeAt(i);
+        }
+    }
+
+    /** Dumps state for debugging purposes. */
+    void dump(@NonNull PrintWriter fout) {
+        int notifiedIssuesCount = mNotifiedIssues.size();
+        fout.println("NOTIFICATION SENDER (" + notifiedIssuesCount + " notified issues)");
+        for (int i = 0; i < notifiedIssuesCount; i++) {
+            SafetyCenterIssueKey key = mNotifiedIssues.keyAt(i);
+            SafetySourceIssue issue = mNotifiedIssues.valueAt(i);
+            fout.println("\t[" + i + "] " + toUserFriendlyString(key) + " -> " + issue);
+        }
+        fout.println();
+    }
+
+    /** Get all of the key-issue pairs for which notifications should be posted or updated now. */
+    @NonNull
+    private ArrayMap<SafetyCenterIssueKey, SafetySourceIssue> getIssuesToNotify(
+            @UserIdInt int userId) {
+        ArrayMap<SafetyCenterIssueKey, SafetySourceIssue> result = new ArrayMap<>();
+        List<SafetyCenterIssueKey> allIssueKeys = mIssueCache.getIssuesForUser(userId);
+
+        for (int i = 0; i < allIssueKeys.size(); i++) {
+            SafetyCenterIssueKey issueKey = allIssueKeys.get(i);
+
+            if (!areNotificationsAllowed(issueKey.getSafetySourceId())) {
+                // Notifications are not allowed for this source
+                continue;
+            }
+
+            // TODO(b/259084807): Consider extracting this policy to a separate class
+            Instant dismissedAt = mIssueCache.getNotificationDismissedAt(issueKey);
+            if (dismissedAt != null) {
+                // Issue was previously dismissed and is skipped
+                continue;
+            }
+
+            // Now retrieve the issue itself and use it to determine the behavior:
+            SafetySourceIssue issue = mSafetyCenterRepository.getSafetySourceIssue(issueKey);
+            int behavior = getBehavior(issue, issueKey);
+            if (behavior == NOTIFICATION_BEHAVIOR_INTERNAL_IMMEDIATELY) {
+                result.put(issueKey, issue);
+            }
+            // TODO(b/259094736): handle delayed notifications using a scheduled job to post later
+        }
+        return result;
+    }
+
+    @NotificationBehaviorInternal
+    private int getBehavior(
+            @NonNull SafetySourceIssue issue, @NonNull SafetyCenterIssueKey issueKey) {
+        if (SdkLevel.isAtLeastU()) {
+            switch (issue.getNotificationBehavior()) {
+                case SafetySourceIssue.NOTIFICATION_BEHAVIOR_NEVER:
+                    return NOTIFICATION_BEHAVIOR_INTERNAL_NEVER;
+                case SafetySourceIssue.NOTIFICATION_BEHAVIOR_DELAYED:
+                    return NOTIFICATION_BEHAVIOR_INTERNAL_DELAYED;
+                case SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY:
+                    return NOTIFICATION_BEHAVIOR_INTERNAL_IMMEDIATELY;
+            }
+        }
+        // On Android T all issues are assumed to have "unspecified" behavior
+        return getBehaviorForIssueWithUnspecifiedBehavior(issueKey);
+    }
+
+    @NotificationBehaviorInternal
+    private int getBehaviorForIssueWithUnspecifiedBehavior(@NonNull SafetyCenterIssueKey issueKey) {
+        // TODO(b/259083775): Make this implementation more useful/complex
+        return NOTIFICATION_BEHAVIOR_INTERNAL_IMMEDIATELY;
+    }
+
+    private boolean areNotificationsAllowed(@NonNull String sourceId) {
+        if (SdkLevel.isAtLeastU()) {
+            SafetyCenterConfigReader.ExternalSafetySource externalSafetySource =
+                    mSafetyCenterConfigReader.getExternalSafetySource(sourceId);
+            if (externalSafetySource != null
+                    && externalSafetySource.getSafetySource().areNotificationsAllowed()) {
+                return true;
+            }
+        }
+        return SafetyCenterFlags.getNotificationsAllowedSourceIds().contains(sourceId);
+    }
+
+    private boolean postNotificationForIssue(
+            @NonNull NotificationManager notificationManager,
+            @NonNull SafetySourceIssue safetySourceIssue,
+            @NonNull SafetyCenterIssueKey key) {
+        Notification notification =
+                mNotificationFactory.newNotificationForIssue(
+                        notificationManager, safetySourceIssue, key);
+        if (notification == null) {
+            // Could not make a Notification for this issue!
+            return false;
+        }
+        // The fixed notification ID is OK because notifications are keyed by (tag, id)
+        String tag = getNotificationTag(key);
+        boolean wasPosted = notifyFromSystem(notificationManager, tag, notification);
+        if (wasPosted) {
+            mNotifiedIssues.put(key, safetySourceIssue);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private void cancelStaleNotifications(
+            @NonNull NotificationManager notificationManager,
+            @UserIdInt int userId,
+            @NonNull ArraySet<SafetyCenterIssueKey> freshIssueKeys) {
+        // Loop in reverse index order to be able to remove entries while iterating
+        for (int i = mNotifiedIssues.size() - 1; i >= 0; i--) {
+            SafetyCenterIssueKey key = mNotifiedIssues.keyAt(i);
+            if (key.getUserId() == userId && !freshIssueKeys.contains(key)) {
+                // Notification should no longer be shown
+                String tag = getNotificationTag(key);
+                cancelNotificationFromSystem(notificationManager, tag);
+                mNotifiedIssues.removeAt(i);
+            }
+        }
+    }
+
+    @NonNull
+    private static String getNotificationTag(@NonNull SafetyCenterIssueKey issueKey) {
+        // TODO(b/259084094): Make this tag creation more robust
+        return issueKey.getSafetySourceId() + ":" + issueKey.getSafetySourceIssueId();
+    }
+
+    /** Returns a {@link NotificationManager} which will send notifications to the given user. */
+    @Nullable
+    private NotificationManager getNotificationManagerForUser(@UserIdInt int recipientUserId) {
+        Context contextAsUser = mContext.createContextAsUser(UserHandle.of(recipientUserId), 0);
+        return contextAsUser.getSystemService(NotificationManager.class);
+    }
+
+    /**
+     * Sends a {@link Notification} from the system, dropping any calling identity. Returns {@code
+     * true} if successful or {@code false} otherwise.
+     *
+     * <p>The recipient of the notification depends on the {@link Context} of the given {@link
+     * NotificationManager}. Use {@link #getNotificationManagerForUser(int)} to send notifications
+     * to a specific user.
+     */
+    private boolean notifyFromSystem(
+            @NonNull NotificationManager notificationManager,
+            @Nullable String tag,
+            @NonNull Notification notification) {
+        // This call is needed to send a notification from the system and this also grants the
+        // necessary POST_NOTIFICATIONS permission.
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            notificationManager.notify(tag, FIXED_NOTIFICATION_ID, notification);
+            return true;
+        } catch (Throwable e) {
+            Log.w(TAG, "Unable to send system notification", e);
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    /**
+     * Cancels a {@link Notification} from the system, dropping any calling identity.
+     *
+     * <p>The recipient of the notification depends on the {@link Context} of the given {@link
+     * NotificationManager}. Use {@link #getNotificationManagerForUser(int)} to cancel notifications
+     * sent to a specific user.
+     */
+    private void cancelNotificationFromSystem(
+            @NonNull NotificationManager notificationManager, @Nullable String tag) {
+        // This call is needed to cancel a notification previously sent from the system
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            notificationManager.cancel(tag, FIXED_NOTIFICATION_ID);
+        } catch (Throwable e) {
+            Log.w(TAG, "Unable to cancel system notification", e);
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+}
diff --git a/service/java/com/android/safetycenter/SafetyCenterPullAtomCallback.java b/service/java/com/android/safetycenter/SafetyCenterPullAtomCallback.java
index 0a847d8..d022a0d 100644
--- a/service/java/com/android/safetycenter/SafetyCenterPullAtomCallback.java
+++ b/service/java/com/android/safetycenter/SafetyCenterPullAtomCallback.java
@@ -39,6 +39,7 @@
 import androidx.annotation.RequiresApi;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.modules.utils.build.SdkLevel;
 import com.android.permission.PermissionStatsLog;
 import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
 
@@ -117,11 +118,14 @@
             Log.i(TAG, "Pulling and writing atoms…");
             for (int i = 0; i < userProfileGroups.size(); i++) {
                 UserProfileGroup userProfileGroup = userProfileGroups.get(i);
-                statsEvents.add(createOverallSafetyStateAtomLocked(userProfileGroup));
+                List<SafetySourcesGroup> loggableGroups =
+                        mSafetyCenterConfigReader.getLoggableSafetySourcesGroups();
+                statsEvents.add(
+                        createOverallSafetyStateAtomLocked(userProfileGroup, loggableGroups));
                 // The SAFETY_SOURCE_STATE_COLLECTED atoms are written instead of being pulled,
                 // they do not support pull but we want to collect them at the same time as
                 // the above pulled atom.
-                writeSafetySourceStateCollectedAtomsLocked(userProfileGroup);
+                writeSafetySourceStateCollectedAtomsLocked(userProfileGroup, loggableGroups);
             }
         }
         return StatsManager.PULL_SUCCESS;
@@ -130,39 +134,49 @@
     @GuardedBy("mApiLock")
     @NonNull
     private StatsEvent createOverallSafetyStateAtomLocked(
-            @NonNull UserProfileGroup userProfileGroup) {
-        SafetyCenterData safetyCenterData =
-                mSafetyCenterDataFactory.getSafetyCenterData("android", userProfileGroup);
-        long openIssuesCount = safetyCenterData.getIssues().size();
-        long dismissedIssuesCount =
-                mSafetyCenterIssueCache.countActiveIssues(userProfileGroup) - openIssuesCount;
+            @NonNull UserProfileGroup userProfileGroup,
+            @NonNull List<SafetySourcesGroup> loggableGroups) {
+        SafetyCenterData loggableData =
+                mSafetyCenterDataFactory.assembleSafetyCenterData(
+                        "android", userProfileGroup, loggableGroups);
+        long openIssuesCount = loggableData.getIssues().size();
+        long dismissedIssuesCount = getDismissedIssuesCountLocked(loggableData, userProfileGroup);
 
         return mStatsdLogger.createSafetyStateEvent(
-                safetyCenterData.getStatus().getSeverityLevel(),
-                openIssuesCount,
-                dismissedIssuesCount);
+                loggableData.getStatus().getSeverityLevel(), openIssuesCount, dismissedIssuesCount);
+    }
+
+    @GuardedBy("mApiLock")
+    private long getDismissedIssuesCountLocked(
+            @NonNull SafetyCenterData loggableData, @NonNull UserProfileGroup userProfileGroup) {
+        if (SdkLevel.isAtLeastU()) {
+            return loggableData.getDismissedIssues().size();
+        }
+        long openIssuesCount = loggableData.getIssues().size();
+        return mSafetyCenterIssueCache.countActiveLoggableIssues(userProfileGroup)
+                - openIssuesCount;
     }
 
     @GuardedBy("mApiLock")
     private void writeSafetySourceStateCollectedAtomsLocked(
-            @NonNull UserProfileGroup userProfileGroup) {
-        List<SafetySourcesGroup> safetySourcesGroups =
-                mSafetyCenterConfigReader.getSafetySourcesGroups();
-        for (int i = 0; i < safetySourcesGroups.size(); i++) {
-            SafetySourcesGroup safetySourcesGroup = safetySourcesGroups.get(i);
-            List<SafetySource> safetySources = safetySourcesGroup.getSafetySources();
+            @NonNull UserProfileGroup userProfileGroup,
+            @NonNull List<SafetySourcesGroup> loggableGroups) {
+        for (int i = 0; i < loggableGroups.size(); i++) {
+            List<SafetySource> loggableSources = loggableGroups.get(i).getSafetySources();
 
-            for (int j = 0; j < safetySources.size(); j++) {
-                SafetySource safetySource = safetySources.get(j);
+            for (int j = 0; j < loggableSources.size(); j++) {
+                SafetySource loggableSource = loggableSources.get(j);
 
-                if (!SafetySources.isExternal(safetySource) || !safetySource.isLoggingAllowed()) {
+                if (!SafetySources.isExternal(loggableSource)) {
                     continue;
                 }
 
                 writeSafetySourceStateCollectedAtomLocked(
-                        safetySource.getId(), userProfileGroup.getProfileParentUserId(), false);
+                        loggableSource.getId(),
+                        userProfileGroup.getProfileParentUserId(),
+                        /* isUserManaged= */ false);
 
-                if (!SafetySources.supportsManagedProfiles(safetySource)) {
+                if (!SafetySources.supportsManagedProfiles(loggableSource)) {
                     continue;
                 }
 
@@ -170,7 +184,9 @@
                         userProfileGroup.getManagedRunningProfilesUserIds();
                 for (int k = 0; k < managedRunningProfilesUserIds.length; k++) {
                     writeSafetySourceStateCollectedAtomLocked(
-                            safetySource.getId(), managedRunningProfilesUserIds[k], true);
+                            loggableSource.getId(),
+                            managedRunningProfilesUserIds[k],
+                            /* isUserManaged= */ true);
                 }
             }
         }
diff --git a/service/java/com/android/safetycenter/SafetyCenterRefreshTracker.java b/service/java/com/android/safetycenter/SafetyCenterRefreshTracker.java
index 7cf538b..e9c9d07 100644
--- a/service/java/com/android/safetycenter/SafetyCenterRefreshTracker.java
+++ b/service/java/com/android/safetycenter/SafetyCenterRefreshTracker.java
@@ -161,7 +161,9 @@
         }
 
         Log.v(TAG, "Refresh with id: " + mRefreshInProgress.getId() + " completed");
-        int wholeResult = toSystemEventResult(mRefreshInProgress.hasAnyTrackedSourceErrors());
+        int wholeResult =
+                toSystemEventResult(
+                        /* success = */ !mRefreshInProgress.hasAnyTrackedSourceErrors());
         mStatsdLogger.writeWholeRefreshSystemEvent(
                 requestType, mRefreshInProgress.getDurationSinceStart(), wholeResult);
         mRefreshInProgress = null;
diff --git a/service/java/com/android/safetycenter/SafetyCenterRepository.java b/service/java/com/android/safetycenter/SafetyCenterRepository.java
index c42d849..2e5410b 100644
--- a/service/java/com/android/safetycenter/SafetyCenterRepository.java
+++ b/service/java/com/android/safetycenter/SafetyCenterRepository.java
@@ -25,6 +25,8 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
 import android.os.SystemClock;
 import android.safetycenter.SafetyCenterData;
 import android.safetycenter.SafetyEvent;
@@ -40,6 +42,7 @@
 
 import androidx.annotation.RequiresApi;
 
+import com.android.modules.utils.build.SdkLevel;
 import com.android.permission.util.UserUtils;
 import com.android.safetycenter.internaldata.SafetyCenterIssueActionId;
 import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
@@ -48,6 +51,7 @@
 import java.time.Duration;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 import javax.annotation.concurrent.NotThreadSafe;
 
@@ -76,6 +80,7 @@
     @NonNull private final SafetyCenterRefreshTracker mSafetyCenterRefreshTracker;
     @NonNull private final StatsdLogger mStatsdLogger;
     @NonNull private final SafetyCenterIssueCache mSafetyCenterIssueCache;
+    @NonNull private final PackageManager mPackageManager;
 
     SafetyCenterRepository(
             @NonNull Context context,
@@ -88,6 +93,7 @@
         mSafetyCenterRefreshTracker = safetyCenterRefreshTracker;
         mStatsdLogger = statsdLogger;
         mSafetyCenterIssueCache = safetyCenterIssueCache;
+        mPackageManager = mContext.getPackageManager();
     }
 
     /**
@@ -163,6 +169,9 @@
      *
      * <p>This method does not perform any validation, {@link #getSafetySourceData(String, String,
      * int)} should be called wherever validation is required.
+     *
+     * <p>Returns {@code null} if it was never set since boot, or if the entry was evicted using
+     * {@link #setSafetySourceData} with a {@code null} value.
      */
     @Nullable
     SafetySourceData getSafetySourceData(@NonNull SafetySourceKey safetySourceKey) {
@@ -444,17 +453,7 @@
         }
 
         SafetySource safetySource = externalSafetySource.getSafetySource();
-
-        // TODO(b/222330089): Security: check certs?
-        if (!packageName.equals(safetySource.getPackageName())) {
-            throw new IllegalArgumentException(
-                    "Unexpected package name: "
-                            + packageName
-                            + ", for safety source: "
-                            + safetySourceId);
-        }
-
-        // TODO(b/222327845): Security: check package is installed for user?
+        validateCallingPackage(safetySource, packageName, safetySourceId);
 
         if (UserUtils.isManagedProfile(userId, mContext)
                 && !SafetySources.supportsManagedProfiles(safetySource)) {
@@ -484,12 +483,12 @@
         if (safetySourceStatus != null) {
             int sourceSeverityLevel = safetySourceStatus.getSeverityLevel();
 
-            if (externalSafetySource.hasEntryInRigidGroup()
+            if (externalSafetySource.hasEntryInStatelessGroup()
                     && sourceSeverityLevel != SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED) {
                 throw new IllegalArgumentException(
                         "Safety source: "
                                 + safetySourceId
-                                + " is in a rigid group but specified a severity level: "
+                                + " is in a stateless group but specified a severity level: "
                                 + sourceSeverityLevel);
             }
 
@@ -533,6 +532,59 @@
         return mSafetyCenterConfigReader.isExternalSafetySourceActive(safetySourceId);
     }
 
+    private void validateCallingPackage(
+            @NonNull SafetySource safetySource,
+            @NonNull String packageName,
+            @NonNull String safetySourceId) {
+        if (!packageName.equals(safetySource.getPackageName())) {
+            throw new IllegalArgumentException(
+                    "Unexpected package name: "
+                            + packageName
+                            + ", for safety source: "
+                            + safetySourceId);
+        }
+
+        // TODO(b/222327845): Security: check package is installed for user?
+
+        if (!SdkLevel.isAtLeastU()) {
+            // No more validation checks possible on T devices
+            return;
+        }
+
+        Set<String> certificateHashes = safetySource.getPackageCertificateHashes();
+        if (certificateHashes.isEmpty()) {
+            Log.d(TAG, "No cert check requested for package " + packageName);
+            return;
+        }
+
+        boolean hasMatchingCert = false;
+        for (String certHash : certificateHashes) {
+            try {
+                byte[] certificate = new Signature(certHash).toByteArray();
+                if (mPackageManager.hasSigningCertificate(
+                        packageName, certificate, PackageManager.CERT_INPUT_SHA256)) {
+                    Log.d(TAG, "Package " + packageName + " has expected signature");
+                    hasMatchingCert = true;
+                }
+            } catch (IllegalArgumentException e) {
+                Log.w(TAG, "Failed to parse signing certificate: " + certHash, e);
+                throw new IllegalStateException(
+                        "Failed to parse signing certificate: " + certHash, e);
+            }
+        }
+
+        if (!hasMatchingCert) {
+            Log.e(
+                    TAG,
+                    "Package "
+                            + packageName
+                            + " for source "
+                            + safetySourceId
+                            + " signed with invalid signature");
+            throw new IllegalArgumentException("Invalid signature for package " + packageName);
+        }
+    }
+
     private boolean processSafetyEvent(
             @NonNull String safetySourceId,
             @NonNull SafetyEvent safetyEvent,
diff --git a/service/java/com/android/safetycenter/SafetyCenterService.java b/service/java/com/android/safetycenter/SafetyCenterService.java
index e522f8c..ec1e18c 100644
--- a/service/java/com/android/safetycenter/SafetyCenterService.java
+++ b/service/java/com/android/safetycenter/SafetyCenterService.java
@@ -19,8 +19,10 @@
 import static android.Manifest.permission.MANAGE_SAFETY_CENTER;
 import static android.Manifest.permission.READ_SAFETY_CENTER_STATUS;
 import static android.Manifest.permission.SEND_SAFETY_CENTER_UPDATE;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
 import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_OTHER;
 import static android.safetycenter.SafetyCenterManager.RefreshReason;
 import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED;
@@ -71,6 +73,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.modules.utils.BackgroundThread;
+import com.android.modules.utils.build.SdkLevel;
 import com.android.permission.util.ForegroundThread;
 import com.android.permission.util.UserUtils;
 import com.android.safetycenter.internaldata.SafetyCenterIds;
@@ -114,11 +117,6 @@
     /** The name of the file used to persist the Safety Center issue cache. */
     private static final String SAFETY_CENTER_ISSUES_CACHE_FILE_NAME = "safety_center_issues.xml";
 
-    /** The START_TASKS_FROM_RECENTS permission. */
-    // TODO(b/242905922): Remove once in API.
-    private static final String START_TASKS_FROM_RECENTS =
-            "android.permission.START_TASKS_FROM_RECENTS";
-
     /** The time delay used to throttle and aggregate writes to disk. */
     private static final Duration WRITE_DELAY = Duration.ofMillis(500);
 
@@ -145,6 +143,10 @@
 
     @GuardedBy("mApiLock")
     @NonNull
+    private final PendingIntentFactory mPendingIntentFactory;
+
+    @GuardedBy("mApiLock")
+    @NonNull
     private final SafetyCenterDataFactory mSafetyCenterDataFactory;
 
     @GuardedBy("mApiLock")
@@ -152,6 +154,10 @@
     private final SafetyCenterListeners mSafetyCenterListeners;
 
     @GuardedBy("mApiLock")
+    @NonNull
+    private final SafetyCenterNotificationSender mNotificationSender;
+
+    @GuardedBy("mApiLock")
     private boolean mSafetyCenterIssueCacheWriteScheduled;
 
     @GuardedBy("mApiLock")
@@ -176,6 +182,7 @@
         StatsdLogger statsdLogger = new StatsdLogger(context, mSafetyCenterConfigReader);
         mSafetyCenterRefreshTracker = new SafetyCenterRefreshTracker(statsdLogger);
         mSafetyCenterIssueCache = new SafetyCenterIssueCache(mSafetyCenterConfigReader);
+        mPendingIntentFactory = new PendingIntentFactory(context, mSafetyCenterResourcesContext);
         mSafetyCenterRepository =
                 new SafetyCenterRepository(
                         context,
@@ -188,10 +195,20 @@
                         mSafetyCenterResourcesContext,
                         mSafetyCenterConfigReader,
                         mSafetyCenterRefreshTracker,
-                        new PendingIntentFactory(context),
+                        mPendingIntentFactory,
                         mSafetyCenterIssueCache,
-                        mSafetyCenterRepository);
+                        mSafetyCenterRepository,
+                        SdkLevel.isAtLeastU()
+                                ? new SafetyCenterIssueDeduplicator(mSafetyCenterIssueCache)
+                                : null);
         mSafetyCenterListeners = new SafetyCenterListeners(mSafetyCenterDataFactory);
+        mNotificationSender =
+                new SafetyCenterNotificationSender(
+                        context,
+                        new SafetyCenterNotificationFactory(context),
+                        mSafetyCenterIssueCache,
+                        mSafetyCenterRepository,
+                        mSafetyCenterConfigReader);
         mSafetyCenterBroadcastDispatcher =
                 new SafetyCenterBroadcastDispatcher(
                         context, mSafetyCenterConfigReader, mSafetyCenterRefreshTracker);
@@ -211,6 +228,9 @@
                                 Resources.getSystem()
                                         .getIdentifier(
                                                 "config_enableSafetyCenter", "bool", "android"));
+        if (!mDeviceSupportsSafetyCenter) {
+            Log.v(TAG, "Device does not support safety center, safety center will be disabled.");
+        }
     }
 
     @Override
@@ -222,6 +242,8 @@
                 if (mConfigAvailable) {
                     readSafetyCenterIssueCacheFileLocked();
                     new UserBroadcastReceiver().register(getContext());
+                    new SafetyCenterNotificationReceiver(mSafetyCenterIssueCache, mApiLock)
+                            .register(getContext());
                 }
             }
         }
@@ -287,6 +309,9 @@
                                 safetySourceData, safetySourceId, safetyEvent, packageName, userId);
                 mSafetyCenterListeners.deliverUpdateForUserProfileGroup(
                         userProfileGroup, hasUpdate, null);
+                if (hasUpdate) {
+                    mNotificationSender.updateNotifications(userId);
+                }
                 scheduleWriteSafetyCenterIssueCacheFileIfNeededLocked();
             }
         }
@@ -348,6 +373,9 @@
                 }
                 mSafetyCenterListeners.deliverUpdateForUserProfileGroup(
                         userProfileGroup, hasUpdate, safetyCenterErrorDetails);
+                if (hasUpdate) {
+                    mNotificationSender.updateNotifications(userId);
+                }
             }
         }
 
@@ -363,6 +391,23 @@
         }
 
         @Override
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        public void refreshSpecificSafetySources(
+                @RefreshReason int refreshReason,
+                @UserIdInt int userId,
+                @NonNull List<String> safetySourceIds) {
+            getContext()
+                    .enforceCallingPermission(MANAGE_SAFETY_CENTER, "refreshSpecificSafetySources");
+            requireNonNull(safetySourceIds, "safetySourceIds cannot be null");
+            RefreshReasons.validate(refreshReason);
+            if (!enforceCrossUserPermission("refreshSpecificSafetySources", userId)
+                    || !checkApiEnabled("refreshSpecificSafetySources")) {
+                return;
+            }
+            startRefreshingSafetySources(refreshReason, userId, safetySourceIds);
+        }
+
+        @Override
         @Nullable
         public SafetyCenterConfig getSafetyCenterConfig() {
             getContext()
@@ -396,7 +441,8 @@
             UserProfileGroup userProfileGroup = UserProfileGroup.from(getContext(), userId);
 
             synchronized (mApiLock) {
-                return mSafetyCenterDataFactory.getSafetyCenterData(packageName, userProfileGroup);
+                return mSafetyCenterDataFactory.assembleSafetyCenterData(
+                        packageName, userProfileGroup);
             }
         }
 
@@ -418,14 +464,15 @@
 
             UserProfileGroup userProfileGroup = UserProfileGroup.from(getContext(), userId);
             synchronized (mApiLock) {
-                boolean registered =
+                IOnSafetyCenterDataChangedListener registeredListener =
                         mSafetyCenterListeners.addListener(listener, packageName, userId);
-                if (!registered) {
+                if (registeredListener == null) {
                     return;
                 }
                 SafetyCenterListeners.deliverUpdateForListener(
-                        listener,
-                        mSafetyCenterDataFactory.getSafetyCenterData(packageName, userProfileGroup),
+                        registeredListener,
+                        mSafetyCenterDataFactory.assembleSafetyCenterData(
+                                packageName, userProfileGroup),
                         null);
             }
         }
@@ -494,6 +541,7 @@
                 }
                 mSafetyCenterListeners.deliverUpdateForUserProfileGroup(
                         userProfileGroup, true, null);
+                mNotificationSender.updateNotifications(userId);
             }
         }
 
@@ -527,51 +575,10 @@
                     "executeSafetyCenterIssueAction",
                     userProfileGroup,
                     safetyCenterIssueKey.getUserId());
-            synchronized (mApiLock) {
-                SafetySourceIssue.Action safetySourceIssueAction =
-                        mSafetyCenterRepository.getSafetySourceIssueAction(
-                                safetyCenterIssueActionId);
-                if (safetySourceIssueAction == null) {
-                    Log.w(
-                            TAG,
-                            "Attempt to execute an issue action that is not provided by the source,"
-                                    + " that was dismissed, or is already in flight");
-                    // Don't send the error to the UI here, since it could happen when clicking the
-                    // button multiple times in a row.
-                    return;
-                }
-
-                Integer taskId =
-                        safetyCenterIssueId.hasTaskId() ? safetyCenterIssueId.getTaskId() : null;
-                if (!dispatchPendingIntent(safetySourceIssueAction.getPendingIntent(), taskId)) {
-                    Log.w(
-                            TAG,
-                            "Error dispatching action: "
-                                    + toUserFriendlyString(safetyCenterIssueActionId));
-                    CharSequence errorMessage;
-                    if (safetySourceIssueAction.willResolve()) {
-                        errorMessage =
-                                mSafetyCenterResourcesContext.getStringByName(
-                                        "resolving_action_error");
-                    } else {
-                        errorMessage =
-                                mSafetyCenterResourcesContext.getStringByName("redirecting_error");
-                    }
-                    mSafetyCenterListeners.deliverUpdateForUserProfileGroup(
-                            userProfileGroup, false, new SafetyCenterErrorDetails(errorMessage));
-                    return;
-                }
-                if (safetySourceIssueAction.willResolve()) {
-                    mSafetyCenterRepository.markSafetyCenterIssueActionInFlight(
-                            safetyCenterIssueActionId);
-                    ResolvingActionTimeout resolvingActionTimeout =
-                            new ResolvingActionTimeout(safetyCenterIssueActionId, userProfileGroup);
-                    mSafetyCenterTimeouts.add(
-                            resolvingActionTimeout, SafetyCenterFlags.getResolvingActionTimeout());
-                    mSafetyCenterListeners.deliverUpdateForUserProfileGroup(
-                            userProfileGroup, true, null);
-                }
-            }
+            Integer taskId =
+                    safetyCenterIssueId.hasTaskId() ? safetyCenterIssueId.getTaskId() : null;
+            executeIssueActionInternal(
+                    safetyCenterIssueKey, safetyCenterIssueActionId, userProfileGroup, taskId);
         }
 
         @Override
@@ -694,33 +701,14 @@
             }
         }
 
-        private boolean dispatchPendingIntent(
-                @NonNull PendingIntent pendingIntent, @Nullable Integer taskId) {
-            try {
-                if (taskId != null
-                        && pendingIntent.isActivity()
-                        && getContext().checkCallingOrSelfPermission(START_TASKS_FROM_RECENTS)
-                                == PERMISSION_GRANTED) {
-                    ActivityOptions options = ActivityOptions.makeBasic();
-                    options.setLaunchTaskId(taskId);
-                    pendingIntent.send(null, 0, null, null, null, null, options.toBundle());
-                } else {
-                    pendingIntent.send();
-                }
-                return true;
-            } catch (PendingIntent.CanceledException ex) {
-                Log.w(TAG, "Couldn't dispatch PendingIntent", ex);
-                return false;
-            }
-        }
-
         @Override
         public int handleShellCommand(
                 @NonNull ParcelFileDescriptor in,
                 @NonNull ParcelFileDescriptor out,
                 @NonNull ParcelFileDescriptor err,
                 @NonNull String[] args) {
-            return new SafetyCenterShellCommandHandler(this)
+            return new SafetyCenterShellCommandHandler(
+                            getContext(), this, mDeviceSupportsSafetyCenter)
                     .exec(
                             this,
                             in.getFileDescriptor(),
@@ -745,6 +733,7 @@
                 mSafetyCenterRefreshTracker.dump(fout);
                 mSafetyCenterTimeouts.dump(fout);
                 mSafetyCenterListeners.dump(fout);
+                mNotificationSender.dump(fout);
             }
         }
 
@@ -800,9 +789,12 @@
 
         private void setInitialState() {
             mSafetyCenterEnabled = SafetyCenterFlags.getSafetyCenterEnabled();
+            Log.v(TAG, "SafetyCenter is " + (mSafetyCenterEnabled ? "enabled." : "disabled."));
         }
 
         private void onSafetyCenterEnabledChanged(boolean safetyCenterEnabled) {
+            Log.v(TAG, "SafetyCenter is now " + (safetyCenterEnabled ? "enabled." : "disabled."));
+
             if (safetyCenterEnabled) {
                 onApiEnabled();
             } else {
@@ -993,19 +985,27 @@
             mSafetyCenterListeners.clearForUser(userId);
             mSafetyCenterRefreshTracker.clearRefreshForUser(userId);
             mSafetyCenterListeners.deliverUpdateForUserProfileGroup(userProfileGroup, true, null);
+            mNotificationSender.updateNotifications(userId);
             scheduleWriteSafetyCenterIssueCacheFileIfNeededLocked();
         }
     }
 
     private void startRefreshingSafetySources(
             @RefreshReason int refreshReason, @UserIdInt int userId) {
+        startRefreshingSafetySources(refreshReason, userId, null);
+    }
+
+    private void startRefreshingSafetySources(
+            @RefreshReason int refreshReason,
+            @UserIdInt int userId,
+            @Nullable List<String> safetySourceIds) {
         UserProfileGroup userProfileGroup = UserProfileGroup.from(getContext(), userId);
         synchronized (mApiLock) {
             mSafetyCenterRepository.clearSafetySourceErrors(userProfileGroup);
 
             String refreshBroadcastId =
                     mSafetyCenterBroadcastDispatcher.sendRefreshSafetySources(
-                            refreshReason, userProfileGroup);
+                            refreshReason, userProfileGroup, safetySourceIds);
             if (refreshBroadcastId == null) {
                 return;
             }
@@ -1019,6 +1019,78 @@
         }
     }
 
+    private void executeIssueActionInternal(
+            @NonNull SafetyCenterIssueKey safetyCenterIssueKey,
+            @NonNull SafetyCenterIssueActionId safetyCenterIssueActionId,
+            @NonNull UserProfileGroup userProfileGroup,
+            @Nullable Integer taskId) {
+        synchronized (mApiLock) {
+            SafetySourceIssue.Action safetySourceIssueAction =
+                    mSafetyCenterRepository.getSafetySourceIssueAction(safetyCenterIssueActionId);
+            if (safetySourceIssueAction == null) {
+                Log.w(
+                        TAG,
+                        "Attempt to execute an issue action that is not provided by the source,"
+                                + " that was dismissed, or is already in flight");
+                // Don't send the error to the UI here, since it could happen when clicking the
+                // button multiple times in a row.
+                return;
+            }
+            PendingIntent issueActionPendingIntent =
+                    mPendingIntentFactory.maybeOverridePendingIntent(
+                            safetyCenterIssueKey.getSafetySourceId(),
+                            safetySourceIssueAction.getPendingIntent(),
+                            false);
+            if (!dispatchPendingIntent(issueActionPendingIntent, taskId)) {
+                Log.w(
+                        TAG,
+                        "Error dispatching action: "
+                                + toUserFriendlyString(safetyCenterIssueActionId));
+                CharSequence errorMessage;
+                if (safetySourceIssueAction.willResolve()) {
+                    errorMessage =
+                            mSafetyCenterResourcesContext.getStringByName("resolving_action_error");
+                } else {
+                    errorMessage =
+                            mSafetyCenterResourcesContext.getStringByName("redirecting_error");
+                }
+                mSafetyCenterListeners.deliverUpdateForUserProfileGroup(
+                        userProfileGroup, false, new SafetyCenterErrorDetails(errorMessage));
+                return;
+            }
+            if (safetySourceIssueAction.willResolve()) {
+                mSafetyCenterRepository.markSafetyCenterIssueActionInFlight(
+                        safetyCenterIssueActionId);
+                ResolvingActionTimeout resolvingActionTimeout =
+                        new ResolvingActionTimeout(safetyCenterIssueActionId, userProfileGroup);
+                mSafetyCenterTimeouts.add(
+                        resolvingActionTimeout, SafetyCenterFlags.getResolvingActionTimeout());
+                mSafetyCenterListeners.deliverUpdateForUserProfileGroup(
+                        userProfileGroup, true, null);
+            }
+        }
+    }
+
+    private boolean dispatchPendingIntent(
+            @NonNull PendingIntent pendingIntent, @Nullable Integer taskId) {
+        try {
+            if (taskId != null
+                    && pendingIntent.isActivity()
+                    && getContext().checkCallingOrSelfPermission(START_TASKS_FROM_RECENTS)
+                            == PERMISSION_GRANTED) {
+                ActivityOptions options = ActivityOptions.makeBasic();
+                options.setLaunchTaskId(taskId);
+                pendingIntent.send(null, 0, null, null, null, null, options.toBundle());
+            } else {
+                pendingIntent.send();
+            }
+            return true;
+        } catch (PendingIntent.CanceledException ex) {
+            Log.w(TAG, "Couldn't dispatch PendingIntent", ex);
+            return false;
+        }
+    }
+
     /** Schedule writing the cache to file. */
     @GuardedBy("mApiLock")
     private void scheduleWriteSafetyCenterIssueCacheFileIfNeededLocked() {
@@ -1078,6 +1150,7 @@
         mSafetyCenterIssueCache.clear();
         mSafetyCenterTimeouts.clear();
         mSafetyCenterRefreshTracker.clearRefresh();
+        mNotificationSender.cancelAllNotifications();
         scheduleWriteSafetyCenterIssueCacheFileIfNeededLocked();
     }
 
diff --git a/service/java/com/android/safetycenter/SafetyCenterShellCommandHandler.java b/service/java/com/android/safetycenter/SafetyCenterShellCommandHandler.java
index d0f972a..a96309b 100644
--- a/service/java/com/android/safetycenter/SafetyCenterShellCommandHandler.java
+++ b/service/java/com/android/safetycenter/SafetyCenterShellCommandHandler.java
@@ -21,6 +21,7 @@
 import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_DEVICE_REBOOT;
 import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_OTHER;
 import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_PAGE_OPEN;
+import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_PERIODIC;
 import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_RESCAN_BUTTON_CLICK;
 import static android.safetycenter.SafetyCenterManager.REFRESH_REASON_SAFETY_CENTER_ENABLED;
 
@@ -29,6 +30,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.content.Context;
 import android.os.RemoteException;
 import android.safetycenter.ISafetyCenterManager;
 import android.safetycenter.SafetyCenterManager.RefreshReason;
@@ -36,21 +38,33 @@
 import androidx.annotation.RequiresApi;
 
 import com.android.modules.utils.BasicShellCommandHandler;
+import com.android.modules.utils.build.SdkLevel;
 
 import java.io.PrintWriter;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
-/** A {@link BasicShellCommandHandler} implementation to handle Safety Center commands. */
+/**
+ * A {@link BasicShellCommandHandler} implementation to handle Safety Center commands.
+ *
+ * <p>Example usage: $ adb shell cmd safety_center refresh --reason PAGE_OPEN --user 10
+ */
 @RequiresApi(TIRAMISU)
 final class SafetyCenterShellCommandHandler extends BasicShellCommandHandler {
 
     @NonNull private static final Map<String, Integer> REASONS = createReasonMap();
 
+    @NonNull private final Context mContext;
     @NonNull private final ISafetyCenterManager mSafetyCenterManager;
+    private final boolean mDeviceSupportsSafetyCenter;
 
-    SafetyCenterShellCommandHandler(@NonNull ISafetyCenterManager safetyCenterManager) {
+    SafetyCenterShellCommandHandler(
+            @NonNull Context context,
+            @NonNull ISafetyCenterManager safetyCenterManager,
+            boolean deviceSupportsSafetyCenter) {
+        mContext = context;
         mSafetyCenterManager = safetyCenterManager;
+        mDeviceSupportsSafetyCenter = deviceSupportsSafetyCenter;
     }
 
     @Override
@@ -64,10 +78,14 @@
             switch (cmd) {
                 case "enabled":
                     return onEnabled();
+                case "supported":
+                    return onSupported();
                 case "refresh":
                     return onRefresh();
                 case "clear-data":
                     return onClearData();
+                case "package-name":
+                    return onPackageName();
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -78,13 +96,13 @@
     }
 
     private int onEnabled() throws RemoteException {
-        if (mSafetyCenterManager.isSafetyCenterEnabled()) {
-            getOutPrintWriter().println("Safety Center is enabled");
-            return 0;
-        } else {
-            getOutPrintWriter().println("Safety Center is not enabled");
-            return 1;
-        }
+        getOutPrintWriter().println(mSafetyCenterManager.isSafetyCenterEnabled());
+        return 0;
+    }
+
+    private int onSupported() {
+        getOutPrintWriter().println(mDeviceSupportsSafetyCenter);
+        return 0;
     }
 
     private int onRefresh() throws RemoteException {
@@ -136,6 +154,12 @@
         return 0;
     }
 
+    private int onPackageName() {
+        getOutPrintWriter()
+                .println(mContext.getPackageManager().getPermissionControllerPackageName());
+        return 0;
+    }
+
     @Override
     public void onHelp() {
         getOutPrintWriter().println("Safety Center (safety_center) commands:");
@@ -143,7 +167,11 @@
         printCmd(
                 "enabled",
                 "Check if Safety Center is enabled",
-                "Exits with status code 0 if enabled or 1 if not enabled");
+                "Prints \"true\" if enabled, \"false\" otherwise");
+        printCmd(
+                "supported",
+                "Check if this device supports Safety Center (i.e. Safety Center could be enabled)",
+                "Prints \"true\" if supported, \"false\" otherwise");
         printCmd(
                 "refresh [--reason REASON] [--user USERID]",
                 "Start a refresh of all sources",
@@ -155,6 +183,7 @@
                 "clear-data",
                 "Clear all data held by Safety Center",
                 "Includes data held in memory and persistent storage but not the listeners.");
+        printCmd("package-name", "Prints the name of the package that contains Safety Center");
     }
 
     /** Helper function to standardise pretty-printing of the help text. */
@@ -176,6 +205,9 @@
         reasons.put("LOCALE_CHANGE", REFRESH_REASON_DEVICE_LOCALE_CHANGE);
         reasons.put("SAFETY_CENTER_ENABLED", REFRESH_REASON_SAFETY_CENTER_ENABLED);
         reasons.put("OTHER", REFRESH_REASON_OTHER);
+        if (SdkLevel.isAtLeastU()) {
+            reasons.put("PERIODIC", REFRESH_REASON_PERIODIC);
+        }
         return unmodifiableMap(reasons);
     }
 }
diff --git a/service/java/com/android/safetycenter/SafetySources.java b/service/java/com/android/safetycenter/SafetySources.java
index d6fc3cf..f57aeb8 100644
--- a/service/java/com/android/safetycenter/SafetySources.java
+++ b/service/java/com/android/safetycenter/SafetySources.java
@@ -25,7 +25,7 @@
 
 import androidx.annotation.RequiresApi;
 
-/** A helper class to facilitate working with {@link SafetySource}. */
+/** A helper class to facilitate working with {@link SafetySource} objects. */
 @RequiresApi(TIRAMISU)
 final class SafetySources {
 
@@ -92,5 +92,20 @@
         return false;
     }
 
+    /**
+     * Returns whether a {@link SafetySource} can be logged, without requiring a check of source
+     * type first.
+     */
+    static boolean isLoggable(@NonNull SafetySource safetySource) {
+        // Only external sources can have logging allowed values. Non-external sources cannot have
+        // their loggability configured. Unfortunately isLoggingAllowed throws if called on a
+        // non-external source.
+        if (isExternal(safetySource)) {
+            return safetySource.isLoggingAllowed();
+        } else {
+            return true;
+        }
+    }
+
     private SafetySources() {}
 }
diff --git a/service/java/com/android/safetycenter/SafetySourcesGroups.java b/service/java/com/android/safetycenter/SafetySourcesGroups.java
new file mode 100644
index 0000000..d446f5a
--- /dev/null
+++ b/service/java/com/android/safetycenter/SafetySourcesGroups.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.safetycenter;
+
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import android.annotation.NonNull;
+import android.safetycenter.config.SafetySourcesGroup;
+
+import androidx.annotation.RequiresApi;
+
+/** Static utilities for working with {@link SafetySourcesGroup} objects. */
+@RequiresApi(TIRAMISU)
+final class SafetySourcesGroups {
+
+    /**
+     * Returns a builder with all fields of the original group copied other than {@link
+     * SafetySourcesGroup#getSafetySources()}.
+     */
+    @NonNull
+    static SafetySourcesGroup.Builder copyToBuilderWithoutSources(
+            @NonNull SafetySourcesGroup group) {
+        return new SafetySourcesGroup.Builder()
+                .setId(group.getId())
+                .setTitleResId(group.getTitleResId())
+                .setSummaryResId(group.getSummaryResId())
+                .setStatelessIconType(group.getStatelessIconType());
+    }
+
+    private SafetySourcesGroups() {}
+}
diff --git a/service/java/com/android/safetycenter/TEST_MAPPING b/service/java/com/android/safetycenter/TEST_MAPPING
index 5feb2e9..c702ee8 100644
--- a/service/java/com/android/safetycenter/TEST_MAPPING
+++ b/service/java/com/android/safetycenter/TEST_MAPPING
@@ -7,11 +7,22 @@
           "exclude-annotation": "com.android.bedstead.harrier.annotations.Postsubmit"
         }
       ]
+    },
+    {
+      "name": "SafetyCenterFunctionalTestCases",
+      "options": [
+        {
+          "exclude-annotation": "com.android.bedstead.harrier.annotations.Postsubmit"
+        }
+      ]
     }
   ],
   "postsubmit": [
     {
       "name": "CtsSafetyCenterTestCases"
+    },
+    {
+      "name": "SafetyCenterFunctionalTestCases"
     }
   ],
   "mainline-presubmit": [
diff --git a/service/java/com/android/safetycenter/UserProfileGroup.java b/service/java/com/android/safetycenter/UserProfileGroup.java
index 264f8bd..777050b 100644
--- a/service/java/com/android/safetycenter/UserProfileGroup.java
+++ b/service/java/com/android/safetycenter/UserProfileGroup.java
@@ -233,7 +233,7 @@
 
     @Override
     public String toString() {
-        return "UserProfiles{"
+        return "UserProfileGroup{"
                 + "mProfileParentUserId="
                 + mProfileParentUserId
                 + ", mManagedProfilesUserIds="
diff --git a/tests/apex/AndroidTest.xml b/tests/apex/AndroidTest.xml
index 283f008..b1af0f5 100644
--- a/tests/apex/AndroidTest.xml
+++ b/tests/apex/AndroidTest.xml
@@ -23,7 +23,7 @@
     <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
 
     <!-- Install test -->
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="test-file-name" value="PermissionApexTests.apk" />
         <option name="cleanup-apks" value="true" />
     </target_preparer>
diff --git a/tests/cts/safetycenter/Android.bp b/tests/cts/safetycenter/Android.bp
index 5c85ea3..3a4e985 100644
--- a/tests/cts/safetycenter/Android.bp
+++ b/tests/cts/safetycenter/Android.bp
@@ -29,6 +29,7 @@
         "src/**/*.kt",
     ],
     static_libs: [
+        "androidx.core_core-ktx",
         "androidx.test.core",
         "androidx.test.ext.junit",
         "androidx.test.ext.truth",
@@ -43,6 +44,7 @@
         "safety-center-config",
         "safety-center-internal-data",
         "safety-center-resources-lib",
+        "modules-utils-build",
         "truth-prebuilt",
         "Nene",
         "Harrier",
diff --git a/tests/cts/safetycenter/AndroidManifest.xml b/tests/cts/safetycenter/AndroidManifest.xml
index c5cd538..5014366 100644
--- a/tests/cts/safetycenter/AndroidManifest.xml
+++ b/tests/cts/safetycenter/AndroidManifest.xml
@@ -39,6 +39,15 @@
             </intent-filter>
         </activity>
 
+        <service android:name=".testing.CtsNotificationListener"
+            android:label="CtsNotificationListener"
+            android:exported="false"
+            android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+            <intent-filter>
+                <action android:name="android.service.notification.NotificationListenerService" />
+            </intent-filter>
+        </service>
+
         <uses-library android:name="android.test.runner"/>
     </application>
 
@@ -52,4 +61,5 @@
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+    <uses-permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"/>
 </manifest>
diff --git a/tests/cts/safetycenter/AndroidTest.xml b/tests/cts/safetycenter/AndroidTest.xml
index 0a1635e..9f51223 100644
--- a/tests/cts/safetycenter/AndroidTest.xml
+++ b/tests/cts/safetycenter/AndroidTest.xml
@@ -32,13 +32,6 @@
 
     <option name="test-suite-tag" value="cts"/>
 
-    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
-        <!-- Avoid restarting device. -->
-        <option name="force-skip-system-props" value="true"/>
-        <!-- Disable syncing to prevent overwriting flags during testing. -->
-        <option name="disable-device-config-sync" value="true" />
-    </target_preparer>
-
     <target_preparer
         class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true"/>
@@ -50,6 +43,9 @@
              aren't polluted by `BOOT_COMPLETED` or similar broadcasts still being delivered, which
              causes our `ActivityManager#waitForBroadcastIdle()` calls to timeout. -->
         <option name="run-command" value="am wait-for-broadcast-idle" />
+        <!-- Disable syncing to prevent overwriting flags during testing. -->
+        <option name="run-command" value="device_config set_sync_disabled_for_tests persistent" />
+        <option name="teardown-command" value="device_config set_sync_disabled_for_tests none" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt
index 88bcf65..56c7c1a 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterDataTest.kt
@@ -19,6 +19,8 @@
 import android.app.PendingIntent
 import android.content.Context
 import android.content.Intent
+import android.os.Build
+import android.os.Bundle
 import android.safetycenter.SafetyCenterData
 import android.safetycenter.SafetyCenterEntry
 import android.safetycenter.SafetyCenterEntryGroup
@@ -27,9 +29,14 @@
 import android.safetycenter.SafetyCenterStaticEntry
 import android.safetycenter.SafetyCenterStaticEntryGroup
 import android.safetycenter.SafetyCenterStatus
+import android.safetycenter.cts.testing.SafetyCenterCtsData.Companion.withDismissedIssuesIfAtLeastU
+import android.safetycenter.cts.testing.SafetyCenterCtsData.Companion.withExtrasIfAtLeastU
+import androidx.core.os.bundleOf
 import androidx.test.core.app.ApplicationProvider
+import androidx.test.core.os.Parcelables.forceParcel
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.ext.truth.os.ParcelableSubject.assertThat
+import androidx.test.filters.SdkSuppress
 import com.android.permission.testing.EqualsHashCodeToStringTester
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.assertFailsWith
@@ -98,12 +105,123 @@
     private val staticEntryGroup2 =
         SafetyCenterStaticEntryGroup("Another static entry group title", listOf(staticEntry2))
 
+    private val filledExtras =
+        Bundle().apply {
+            putBundle(SOURCE_EXTRA_KEY_1, bundleOf(EXTRA_KEY_1 to EXTRA_VALUE_1))
+            putBundle(SOURCE_EXTRA_KEY_2, bundleOf(EXTRA_KEY_2 to EXTRA_VALUE_2))
+        }
+
     private val data1 =
         SafetyCenterData(status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1))
     private val data2 =
         SafetyCenterData(status2, listOf(issue2), listOf(entryOrGroup2), listOf(staticEntryGroup2))
 
     @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getStatus_withDefaultBuilder_returnsStatus() {
+        val safetyCenterData = SafetyCenterData.Builder(status1).build()
+
+        assertThat(safetyCenterData.status).isEqualTo(status1)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getIssues_withDefaultBuilder_returnsEmptyList() {
+        val safetyCenterData = SafetyCenterData.Builder(status1).build()
+
+        assertThat(safetyCenterData.issues).isEmpty()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getIssues_whenSetExplicitly_returnsIssues() {
+        val safetyCenterData =
+            SafetyCenterData.Builder(status1).addIssue(issue1).addIssue(issue2).build()
+
+        assertThat(safetyCenterData.issues).containsExactly(issue1, issue2).inOrder()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getEntriesOrGroups_withDefaultBuilder_returnsEmptyList() {
+        val safetyCenterData = SafetyCenterData.Builder(status1).build()
+
+        assertThat(safetyCenterData.entriesOrGroups).isEmpty()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getEntriesOrGroups_whenSetExplicitly_returnsEntriesOrGroups() {
+        val safetyCenterData =
+            SafetyCenterData.Builder(status1)
+                .addEntryOrGroup(entryOrGroup1)
+                .addEntryOrGroup(entryOrGroup2)
+                .build()
+
+        assertThat(safetyCenterData.entriesOrGroups)
+            .containsExactly(entryOrGroup1, entryOrGroup2)
+            .inOrder()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getStaticGroups_withDefaultBuilder_returnsEmptyList() {
+        val safetyCenterData = SafetyCenterData.Builder(status1).build()
+
+        assertThat(safetyCenterData.staticEntryGroups).isEmpty()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getStaticEntryGroups_whenSetExplicitly_returnsStaticEntryGroups() {
+        val safetyCenterData =
+            SafetyCenterData.Builder(status1)
+                .addStaticEntryGroup(staticEntryGroup1)
+                .addStaticEntryGroup(staticEntryGroup2)
+                .build()
+
+        assertThat(safetyCenterData.staticEntryGroups)
+            .containsExactly(staticEntryGroup1, staticEntryGroup2)
+            .inOrder()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getDismissedIssues_withDefaultBuilder_returnsEmptyList() {
+        val safetyCenterData = SafetyCenterData.Builder(status1).build()
+
+        assertThat(safetyCenterData.dismissedIssues).isEmpty()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getDismissedIssues_whenSetExplicitly_returnsIssues() {
+        val safetyCenterData =
+            SafetyCenterData.Builder(status1)
+                .addDismissedIssue(issue1)
+                .addDismissedIssue(issue2)
+                .build()
+
+        assertThat(safetyCenterData.dismissedIssues).containsExactly(issue1, issue2).inOrder()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getExtras_withDefaultBuilder_returnsEmptyBundle() {
+        val safetyCenterData = SafetyCenterData.Builder(status1).build()
+
+        assertThat(safetyCenterData.extras.keySet()).isEmpty()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getExtras_whenSetExplicitly_returnsExtras() {
+        val safetyCenterData = SafetyCenterData.Builder(status1).setExtras(filledExtras).build()
+
+        assertContainsExtras(safetyCenterData)
+    }
+
+    @Test
     fun getStatus_returnsStatus() {
         assertThat(data1.status).isEqualTo(status1)
         assertThat(data2.status).isEqualTo(status2)
@@ -116,6 +234,23 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getDismissedIssues_returnsDismissedIssues() {
+        val data3 = data1.withDismissedIssuesIfAtLeastU(listOf(issue2))
+
+        assertThat(data1.dismissedIssues).isEmpty()
+        assertThat(data3.dismissedIssues).containsExactly(issue2)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getDismissedIssues_mutationsAreNotAllowed() {
+        val mutatedDismissedIssues = data1.dismissedIssues
+
+        assertFailsWith(UnsupportedOperationException::class) { mutatedDismissedIssues.add(issue2) }
+    }
+
+    @Test
     fun getIssues_mutationsAreNotAllowed() {
         val mutatedIssues = data1.issues
 
@@ -153,6 +288,104 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun builder_addIssue_doesNotMutatePreviouslyBuiltInstance() {
+        val safetyCenterDataBuilder = SafetyCenterData.Builder(status1).addIssue(issue1)
+        val issues = safetyCenterDataBuilder.build().issues
+
+        safetyCenterDataBuilder.addIssue(issue2)
+
+        assertThat(issues).containsExactly(issue1)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun builder_clearIssues_removesAllIssues() {
+        val safetyCenterData =
+            SafetyCenterData.Builder(status1)
+                .addIssue(issue1)
+                .addIssue(issue2)
+                .clearIssues()
+                .build()
+
+        assertThat(safetyCenterData.issues).isEmpty()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun builder_addEntryOrGroup_doesNotMutatePreviouslyBuiltInstance() {
+        val safetyCenterDataBuilder =
+            SafetyCenterData.Builder(status1).addEntryOrGroup(entryOrGroup1)
+        val entriesOrGroups = safetyCenterDataBuilder.build().entriesOrGroups
+
+        safetyCenterDataBuilder.addEntryOrGroup(entryOrGroup2)
+
+        assertThat(entriesOrGroups).containsExactly(entryOrGroup1)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun builder_clearEntriesOrGroups_removesAllEntriesOrGroups() {
+        val safetyCenterData =
+            SafetyCenterData.Builder(status1)
+                .addEntryOrGroup(entryOrGroup1)
+                .addEntryOrGroup(entryOrGroup2)
+                .clearEntriesOrGroups()
+                .build()
+
+        assertThat(safetyCenterData.entriesOrGroups).isEmpty()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun builder_addStaticEntryGroup_doesNotMutatePreviouslyBuiltInstance() {
+        val safetyCenterDataBuilder =
+            SafetyCenterData.Builder(status1).addStaticEntryGroup(staticEntryGroup1)
+        val staticEntryGroups = safetyCenterDataBuilder.build().staticEntryGroups
+
+        safetyCenterDataBuilder.addStaticEntryGroup(staticEntryGroup2)
+
+        assertThat(staticEntryGroups).containsExactly(staticEntryGroup1)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun builder_clearStaticEntryGroups_removesAllStaticEntryGroups() {
+        val safetyCenterData =
+            SafetyCenterData.Builder(status1)
+                .addStaticEntryGroup(staticEntryGroup1)
+                .addStaticEntryGroup(staticEntryGroup2)
+                .clearStaticEntryGroups()
+                .build()
+
+        assertThat(safetyCenterData.staticEntryGroups).isEmpty()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun builder_addDismissedIssue_doesNotMutatePreviouslyBuiltInstance() {
+        val safetyCenterDataBuilder = SafetyCenterData.Builder(status1).addDismissedIssue(issue1)
+        val dismissedIssues = safetyCenterDataBuilder.build().dismissedIssues
+
+        safetyCenterDataBuilder.addDismissedIssue(issue2)
+
+        assertThat(dismissedIssues).containsExactly(issue1)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun builder_clearDismissedIssues_removesAllDismissedIssues() {
+        val safetyCenterData =
+            SafetyCenterData.Builder(status1)
+                .addDismissedIssue(issue1)
+                .addDismissedIssue(issue2)
+                .clearDismissedIssues()
+                .build()
+
+        assertThat(safetyCenterData.dismissedIssues).isEmpty()
+    }
+
+    @Test
     fun describeContents_returns0() {
         assertThat(data1.describeContents()).isEqualTo(0)
         assertThat(data2.describeContents()).isEqualTo(0)
@@ -165,31 +398,196 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun parcelRoundTrip_withDismissedIssues_recreatesEqual() {
+        val data3 = data1.withDismissedIssuesIfAtLeastU(listOf(issue2))
+        val data4 = data2.withDismissedIssuesIfAtLeastU(listOf(issue1))
+
+        assertThat(data3).recreatesEqual(SafetyCenterData.CREATOR)
+        assertThat(data4).recreatesEqual(SafetyCenterData.CREATOR)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun parcelRoundTrip_withExtras_recreatesEqual() {
+        val safetyCenterDataWithExtras = data1.withExtrasIfAtLeastU(filledExtras)
+        val safetyCenterDatafromParcel =
+            forceParcel(safetyCenterDataWithExtras, SafetyCenterData.CREATOR)
+
+        assertThat(safetyCenterDatafromParcel).isEqualTo(safetyCenterDataWithExtras)
+        assertContainsExtras(safetyCenterDatafromParcel)
+    }
+
+    @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        val equalsHashCodeToStringTester =
+            EqualsHashCodeToStringTester.ofParcelable(parcelableCreator = SafetyCenterData.CREATOR)
+                .addEqualityGroup(
+                    data1,
+                    SafetyCenterData(
+                        status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1)))
+                .addEqualityGroup(
+                    data2,
+                    SafetyCenterData(
+                        status2, listOf(issue2), listOf(entryOrGroup2), listOf(staticEntryGroup2)))
+                .addEqualityGroup(
+                    SafetyCenterData(status1, listOf(), listOf(), listOf()),
+                    SafetyCenterData(status1, listOf(), listOf(), listOf()))
+                .addEqualityGroup(
+                    SafetyCenterData(
+                        status2, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1)))
+                .addEqualityGroup(
+                    SafetyCenterData(
+                        status1, listOf(issue2), listOf(entryOrGroup1), listOf(staticEntryGroup1)))
+                .addEqualityGroup(
+                    SafetyCenterData(
+                        status1, listOf(issue1), listOf(entryOrGroup2), listOf(staticEntryGroup1)))
+                .addEqualityGroup(
+                    SafetyCenterData(
+                        status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup2)))
+
+        equalsHashCodeToStringTester.test()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun equalsHashCode_atLeastU_usingEqualsHashCodeToStringTester() {
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetyCenterData.CREATOR,
+                ignoreToString = true,
+                createCopy = { SafetyCenterData.Builder(it).build() })
             .addEqualityGroup(
                 data1,
                 SafetyCenterData(
-                    status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1)))
+                    status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1)),
+                SafetyCenterData.Builder(status1)
+                    .addIssue(issue1)
+                    .addEntryOrGroup(entryOrGroup1)
+                    .addStaticEntryGroup(staticEntryGroup1)
+                    .build(),
+                SafetyCenterData.Builder(status1)
+                    .addIssue(issue1)
+                    .addEntryOrGroup(entryOrGroup1)
+                    .addStaticEntryGroup(staticEntryGroup1)
+                    .setExtras(filledExtras)
+                    .build())
             .addEqualityGroup(
-                data2,
-                SafetyCenterData(
-                    status2, listOf(issue2), listOf(entryOrGroup2), listOf(staticEntryGroup2)))
+                SafetyCenterData.Builder(status1)
+                    .addEntryOrGroup(entryOrGroup1)
+                    .addStaticEntryGroup(staticEntryGroup1)
+                    .addDismissedIssue(issue1)
+                    .build(),
+                SafetyCenterData.Builder(status1)
+                    .addEntryOrGroup(entryOrGroup1)
+                    .addStaticEntryGroup(staticEntryGroup1)
+                    .addDismissedIssue(issue1)
+                    .setExtras(filledExtras)
+                    .build())
             .addEqualityGroup(
-                SafetyCenterData(status1, listOf(), listOf(), listOf()),
-                SafetyCenterData(status1, listOf(), listOf(), listOf()))
+                SafetyCenterData.Builder(status1)
+                    .addEntryOrGroup(entryOrGroup1)
+                    .addStaticEntryGroup(staticEntryGroup1)
+                    .addDismissedIssue(issue2)
+                    .build(),
+                SafetyCenterData.Builder(status1)
+                    .addEntryOrGroup(entryOrGroup1)
+                    .addStaticEntryGroup(staticEntryGroup1)
+                    .addDismissedIssue(issue2)
+                    .setExtras(filledExtras)
+                    .build())
             .addEqualityGroup(
-                SafetyCenterData(
-                    status2, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup1)))
+                SafetyCenterData.Builder(status1)
+                    .addIssue(issue2)
+                    .addEntryOrGroup(entryOrGroup1)
+                    .addStaticEntryGroup(staticEntryGroup1)
+                    .addIssue(issue1)
+                    .setExtras(filledExtras)
+                    .build(),
+                SafetyCenterData.Builder(status1)
+                    .addIssue(issue2)
+                    .addEntryOrGroup(entryOrGroup1)
+                    .addStaticEntryGroup(staticEntryGroup1)
+                    .addIssue(issue1)
+                    .setExtras(filledExtras)
+                    .build())
             .addEqualityGroup(
-                SafetyCenterData(
-                    status1, listOf(issue2), listOf(entryOrGroup1), listOf(staticEntryGroup1)))
+                SafetyCenterData.Builder(status1)
+                    .addIssue(issue1)
+                    .addEntryOrGroup(entryOrGroup1)
+                    .addStaticEntryGroup(staticEntryGroup1)
+                    .addDismissedIssue(issue2)
+                    .build(),
+                SafetyCenterData.Builder(status1)
+                    .addIssue(issue1)
+                    .addEntryOrGroup(entryOrGroup1)
+                    .addStaticEntryGroup(staticEntryGroup1)
+                    .addDismissedIssue(issue2)
+                    .setExtras(filledExtras)
+                    .build())
             .addEqualityGroup(
-                SafetyCenterData(
-                    status1, listOf(issue1), listOf(entryOrGroup2), listOf(staticEntryGroup1)))
-            .addEqualityGroup(
-                SafetyCenterData(
-                    status1, listOf(issue1), listOf(entryOrGroup1), listOf(staticEntryGroup2)))
+                SafetyCenterData.Builder(status1)
+                    .addEntryOrGroup(entryOrGroup1)
+                    .addStaticEntryGroup(staticEntryGroup1)
+                    .addDismissedIssue(issue1)
+                    .addDismissedIssue(issue2)
+                    .build(),
+                SafetyCenterData.Builder(status1)
+                    .addEntryOrGroup(entryOrGroup1)
+                    .addStaticEntryGroup(staticEntryGroup1)
+                    .addDismissedIssue(issue1)
+                    .addDismissedIssue(issue2)
+                    .setExtras(filledExtras)
+                    .build())
             .test()
     }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun toString_withExtras_containsHasExtras() {
+        val safetyCenterDataWithExtras = data1.withExtrasIfAtLeastU(filledExtras)
+
+        val stringRepresentation = safetyCenterDataWithExtras.toString()
+
+        assertThat(stringRepresentation).contains("(has extras)")
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun toString_withoutExtras_doesNotContainHasExtras() {
+        val safetyCenterDataWithoutExtras = data1
+
+        val stringRepresentation = safetyCenterDataWithoutExtras.toString()
+
+        assertThat(stringRepresentation).doesNotContain("(has extras)")
+    }
+
+    private fun assertContainsExtras(data: SafetyCenterData) {
+        assertThat(data.extras.keySet()).containsExactly(SOURCE_EXTRA_KEY_1, SOURCE_EXTRA_KEY_2)
+        val sourceExtra1 = data.extras.getBundle(SOURCE_EXTRA_KEY_1)!!
+        val sourceExtra2 = data.extras.getBundle(SOURCE_EXTRA_KEY_2)!!
+        assertThat(sourceExtra1.keySet()).containsExactly(EXTRA_KEY_1)
+        assertThat(sourceExtra1.getString(EXTRA_KEY_1, "")).isEqualTo(EXTRA_VALUE_1)
+        assertThat(sourceExtra2.keySet()).containsExactly(EXTRA_KEY_2)
+        assertThat(sourceExtra2.getString(EXTRA_KEY_2, "")).isEqualTo(EXTRA_VALUE_2)
+    }
+
+    private companion object {
+        /** Key of extra data in [Bundle]. */
+        const val EXTRA_KEY_1 = "extra_key_1"
+
+        /** Key of extra data in [Bundle]. */
+        const val EXTRA_KEY_2 = "extra_key_2"
+
+        /** Value of extra data in [Bundle]. */
+        const val EXTRA_VALUE_1 = "extra_value_1"
+
+        /** Value of extra data in [Bundle]. */
+        const val EXTRA_VALUE_2 = "extra_value_2"
+
+        /** Key of [SafetySourceData] extra data in combined [Bundle]. */
+        const val SOURCE_EXTRA_KEY_1 = "source_extra_key_1"
+
+        /** Key of [SafetySourceData] extra data in combined [Bundle]. */
+        const val SOURCE_EXTRA_KEY_2 = "source_extra_key_2"
+    }
 }
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt
index 2c69578..2354a60 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryGroupTest.kt
@@ -169,10 +169,11 @@
 
     @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetyCenterEntryGroup.CREATOR,
+                createCopy = { SafetyCenterEntryGroup.Builder(it).build() })
             .addEqualityGroup(
                 entryGroup1,
-                SafetyCenterEntryGroup.Builder(entryGroup1).build(),
                 SafetyCenterEntryGroup.Builder(groupId1, "A group title")
                     .setSummary("A group summary")
                     .setSeverityLevel(SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_OK)
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryOrGroupTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryOrGroupTest.kt
index 7a90798..779968e 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryOrGroupTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryOrGroupTest.kt
@@ -99,7 +99,8 @@
 
     @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetyCenterEntryOrGroup.CREATOR)
             .addEqualityGroup(entryOrGroupWithEntry, SafetyCenterEntryOrGroup(entry1))
             .addEqualityGroup(entryOrGroupWithGroup, SafetyCenterEntryOrGroup(entryGroup1))
             .addEqualityGroup(SafetyCenterEntryOrGroup(entry2))
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt
index c3febe2..2dd5baa 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterEntryTest.kt
@@ -195,8 +195,10 @@
 
     @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
-            .addEqualityGroup(entry1, SafetyCenterEntry.Builder(entry1).build())
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetyCenterEntry.CREATOR,
+                createCopy = { SafetyCenterEntry.Builder(it).build() })
+            .addEqualityGroup(entry1)
             .addEqualityGroup(
                 SafetyCenterEntry.Builder("id", "a title")
                     .setSummary("a summary")
@@ -289,7 +291,8 @@
 
     @Test
     fun iconAction_equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetyCenterEntry.IconAction.CREATOR)
             .addEqualityGroup(
                 iconAction1,
                 SafetyCenterEntry.IconAction(
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorDetailsTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorDetailsTest.kt
index 4bc29b3..788f3ec 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorDetailsTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterErrorDetailsTest.kt
@@ -51,7 +51,8 @@
 
     @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetyCenterErrorDetails.CREATOR)
             .addEqualityGroup(errorDetails1, SafetyCenterErrorDetails("an error message"))
             .addEqualityGroup(errorDetails2, SafetyCenterErrorDetails("another error message"))
             .addEqualityGroup(
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt
index c57b648..e7c72b9 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterIssueTest.kt
@@ -19,13 +19,19 @@
 import android.app.PendingIntent
 import android.content.Context
 import android.content.Intent
+import android.os.Build
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
 import android.safetycenter.SafetyCenterIssue
+import androidx.annotation.RequiresApi
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.ext.truth.os.ParcelableSubject.assertThat
+import androidx.test.filters.SdkSuppress
 import com.android.permission.testing.EqualsHashCodeToStringTester
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.assertFailsWith
+import org.junit.Assume.assumeFalse
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -99,6 +105,33 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getAttributionTitle_returnsAttributionTitle() {
+        assertThat(
+                SafetyCenterIssue.Builder(issue1)
+                    .setAttributionTitle("an attributionTitle")
+                    .build()
+                    .attributionTitle)
+            .isEqualTo("an attributionTitle")
+        assertThat(
+                SafetyCenterIssue.Builder(issue1)
+                    .setAttributionTitle("another attributionTitle")
+                    .build()
+                    .attributionTitle)
+            .isEqualTo("another attributionTitle")
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getAttributionTitle_withNullAttributionTitle_returnsNull() {
+        val safetyCenterIssue =
+            SafetyCenterIssue.Builder("issue_id", "Everything's good", "Please acknowledge this")
+                .build()
+
+        assertThat(safetyCenterIssue.attributionTitle).isNull()
+    }
+
+    @Test
     fun getSeverityLevel_returnsSeverityLevel() {
         assertThat(
                 SafetyCenterIssue.Builder(issue1)
@@ -182,6 +215,64 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getGroupId_withNonNullValue_returnsGroupId() {
+        val issue = SafetyCenterIssue.Builder(issue1).setGroupId("group_id").build()
+
+        assertThat(issue.groupId).isEqualTo("group_id")
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getGroupId_withNullValue_returnsNull() {
+        val issue =
+            SafetyCenterIssue.Builder("issue_id", "Everything's good", "Please acknowledge this")
+                .build()
+
+        assertThat(issue.groupId).isNull()
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = TIRAMISU)
+    fun getGroupId_withVersionLessThanU_throwsUnsupportedOperationException() {
+        // TODO(b/258228790): Remove after U is no longer in pre-release
+        assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
+        val issue =
+            SafetyCenterIssue.Builder("issue_id", "Everything's good", "Please acknowledge this")
+                .build()
+
+        val exception = assertFailsWith(UnsupportedOperationException::class) { issue.groupId }
+
+        assertThat(exception)
+            .hasMessageThat()
+            .isEqualTo("Method not supported for versions lower than UPSIDE_DOWN_CAKE")
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun setGroupId_withNullValue_returnsNull() {
+        val issue = SafetyCenterIssue.Builder(issue1).setGroupId(null).build()
+
+        assertThat(issue.groupId).isNull()
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = TIRAMISU)
+    fun setGroupId_withVersionLessThanU_throwsUnsupportedOperationException() {
+        // TODO(b/258228790): Remove after U is no longer in pre-release
+        assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
+
+        val exception =
+            assertFailsWith(UnsupportedOperationException::class) {
+                SafetyCenterIssue.Builder(issue1).setGroupId("group_id").build()
+            }
+
+        assertThat(exception)
+            .hasMessageThat()
+            .isEqualTo("Method not supported for versions lower than UPSIDE_DOWN_CAKE")
+    }
+
+    @Test
     fun describeContents_returns0() {
         assertThat(issue1.describeContents()).isEqualTo(0)
         assertThat(issueWithRequiredFieldsOnly.describeContents()).isEqualTo(0)
@@ -194,37 +285,31 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun parcelRoundTrip_recreatesEqual_atLeastAndroidU() {
+        val safetyCenterIssue =
+            SafetyCenterIssue.Builder("issue_id", "Everything's good", "Please acknowledge this")
+                .setSubtitle("In the neighborhood")
+                .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK)
+                .setDismissible(true)
+                .setShouldConfirmDismissal(true)
+                .setActions(listOf(action1))
+                .setAttributionTitle("Attribution title")
+                .setGroupId("group_id")
+                .build()
+
+        assertThat(safetyCenterIssue).recreatesEqual(SafetyCenterIssue.CREATOR)
+    }
+
+    @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
-            .addEqualityGroup(issue1, SafetyCenterIssue.Builder(issue1).build())
-            .addEqualityGroup(issueWithRequiredFieldsOnly)
-            .addEqualityGroup(
-                SafetyCenterIssue.Builder("an id", "a title", "Please acknowledge this")
-                    .setSubtitle("In the neighborhood")
-                    .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK)
-                    .setActions(listOf(action1))
-                    .build(),
-                SafetyCenterIssue.Builder("an id", "a title", "Please acknowledge this")
-                    .setSubtitle("In the neighborhood")
-                    .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK)
-                    .setActions(listOf(action1))
-                    .build())
-            .addEqualityGroup(SafetyCenterIssue.Builder(issue1).setId("a different id").build())
-            .addEqualityGroup(
-                SafetyCenterIssue.Builder(issue1).setTitle("a different title").build())
-            .addEqualityGroup(
-                SafetyCenterIssue.Builder(issue1).setSubtitle("a different subtitle").build())
-            .addEqualityGroup(
-                SafetyCenterIssue.Builder(issue1).setSummary("a different summary").build())
-            .addEqualityGroup(
-                SafetyCenterIssue.Builder(issue1)
-                    .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING)
-                    .build())
-            .addEqualityGroup(SafetyCenterIssue.Builder(issue1).setDismissible(false).build())
-            .addEqualityGroup(
-                SafetyCenterIssue.Builder(issue1).setShouldConfirmDismissal(false).build())
-            .addEqualityGroup(SafetyCenterIssue.Builder(issue1).setActions(listOf(action2)).build())
-            .test()
+        newTiramisuEqualsHashCodeToStringTester().test()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun equalsHashCodeToString_usingEqualsHashCodeToStringTester_atLeastAndroidU() {
+        newUpsideDownCakeEqualsHashCodeToStringTester().test()
     }
 
     @Test
@@ -277,7 +362,104 @@
 
     @Test
     fun action_equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        issueActionNewTiramisuEqualsHashCodeToStringTester().test()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun action_equalsHashCodeToString_usingEqualsHashCodeToStringTester_atLeastAndroidU() {
+        issueActionNewTiramisuEqualsHashCodeToStringTester(
+                createCopyFromBuilder = { SafetyCenterIssue.Action.Builder(it).build() })
+            .test()
+    }
+
+    /**
+     * Creates a new [EqualsHashCodeToStringTester] instance with all the equality groups in the
+     * [newTiramisuEqualsHashCodeToStringTester] plus new equality groups covering all the new
+     * fields added in U.
+     */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    private fun newUpsideDownCakeEqualsHashCodeToStringTester():
+        EqualsHashCodeToStringTester<SafetyCenterIssue> {
+        val issueWithTiramisuFields =
+            SafetyCenterIssue.Builder("issue_id", "Everything's good", "Please acknowledge this")
+                .setSubtitle("In the neighborhood")
+                .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK)
+                .setDismissible(true)
+                .setShouldConfirmDismissal(true)
+                .setActions(listOf(action1))
+                .build()
+        return newTiramisuEqualsHashCodeToStringTester(
+                createCopyFromBuilder = { SafetyCenterIssue.Builder(it).build() })
+            .addEqualityGroup(
+                SafetyCenterIssue.Builder(issueWithTiramisuFields)
+                    .setAttributionTitle("Attribution title")
+                    .build(),
+                SafetyCenterIssue.Builder(issueWithTiramisuFields)
+                    .setAttributionTitle("Attribution title")
+                    .build())
+            .addEqualityGroup(
+                SafetyCenterIssue.Builder(issueWithTiramisuFields)
+                    .setAttributionTitle("a different attribution title")
+                    .build())
+            .addEqualityGroup(
+                SafetyCenterIssue.Builder(issueWithTiramisuFields)
+                    .setAttributionTitle("Attribution title")
+                    .setGroupId("group_id")
+                    .build())
+            .addEqualityGroup(
+                SafetyCenterIssue.Builder(issueWithTiramisuFields).setGroupId("group_id").build(),
+                SafetyCenterIssue.Builder(issueWithTiramisuFields).setGroupId("group_id").build())
+            .addEqualityGroup(
+                SafetyCenterIssue.Builder(issueWithTiramisuFields)
+                    .setGroupId("a different group_id")
+                    .build())
+    }
+
+    /**
+     * Creates a new [EqualsHashCodeToStringTester] instance which covers all the fields in the T
+     * API and is safe to use on any T+ API level.
+     */
+    private fun newTiramisuEqualsHashCodeToStringTester(
+        createCopyFromBuilder: ((SafetyCenterIssue) -> SafetyCenterIssue)? = null
+    ) =
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetyCenterIssue.CREATOR, createCopy = createCopyFromBuilder)
+            .addEqualityGroup(issue1, SafetyCenterIssue.Builder(issue1).build())
+            .addEqualityGroup(issueWithRequiredFieldsOnly)
+            .addEqualityGroup(
+                SafetyCenterIssue.Builder("an id", "a title", "Please acknowledge this")
+                    .setSubtitle("In the neighborhood")
+                    .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK)
+                    .setActions(listOf(action1))
+                    .build(),
+                SafetyCenterIssue.Builder("an id", "a title", "Please acknowledge this")
+                    .setSubtitle("In the neighborhood")
+                    .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_OK)
+                    .setActions(listOf(action1))
+                    .build())
+            .addEqualityGroup(SafetyCenterIssue.Builder(issue1).setId("a different id").build())
+            .addEqualityGroup(
+                SafetyCenterIssue.Builder(issue1).setTitle("a different title").build())
+            .addEqualityGroup(
+                SafetyCenterIssue.Builder(issue1).setSubtitle("a different subtitle").build())
+            .addEqualityGroup(
+                SafetyCenterIssue.Builder(issue1).setSummary("a different summary").build())
+            .addEqualityGroup(
+                SafetyCenterIssue.Builder(issue1)
+                    .setSeverityLevel(SafetyCenterIssue.ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING)
+                    .build())
+            .addEqualityGroup(SafetyCenterIssue.Builder(issue1).setDismissible(false).build())
+            .addEqualityGroup(
+                SafetyCenterIssue.Builder(issue1).setShouldConfirmDismissal(false).build())
+            .addEqualityGroup(SafetyCenterIssue.Builder(issue1).setActions(listOf(action2)).build())
+
+    private fun issueActionNewTiramisuEqualsHashCodeToStringTester(
+        createCopyFromBuilder: ((SafetyCenterIssue.Action) -> SafetyCenterIssue.Action)? = null
+    ) =
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetyCenterIssue.Action.CREATOR,
+                createCopy = createCopyFromBuilder)
             .addEqualityGroup(action1)
             .addEqualityGroup(action2)
             .addEqualityGroup(
@@ -327,6 +509,4 @@
                     .setLabel("another_label")
                     .setPendingIntent(pendingIntent2)
                     .build())
-            .test()
-    }
 }
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt
index 87ef29d..a5d1c5f 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt
@@ -17,6 +17,10 @@
 package android.safetycenter.cts
 
 import android.content.Context
+import android.os.Build
+import android.os.Build.VERSION.CODENAME
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
 import android.os.UserHandle.USER_NULL
 import android.safetycenter.SafetyCenterData
 import android.safetycenter.SafetyCenterEntry
@@ -25,7 +29,7 @@
 import android.safetycenter.SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_RECOMMENDATION
 import android.safetycenter.SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNKNOWN
 import android.safetycenter.SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED
-import android.safetycenter.SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR
+import android.safetycenter.SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_INFO
 import android.safetycenter.SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON
 import android.safetycenter.SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_PRIVACY
 import android.safetycenter.SafetyCenterEntryGroup
@@ -35,6 +39,7 @@
 import android.safetycenter.SafetyCenterManager.OnSafetyCenterDataChangedListener
 import android.safetycenter.SafetyCenterManager.REFRESH_REASON_OTHER
 import android.safetycenter.SafetyCenterManager.REFRESH_REASON_PAGE_OPEN
+import android.safetycenter.SafetyCenterManager.REFRESH_REASON_PERIODIC
 import android.safetycenter.SafetyCenterManager.REFRESH_REASON_RESCAN_BUTTON_CLICK
 import android.safetycenter.SafetyCenterStaticEntry
 import android.safetycenter.SafetyCenterStaticEntryGroup
@@ -83,21 +88,27 @@
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_GROUP_ID
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_HIDDEN_ID
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_HIDDEN_WITH_SEARCH_ID
-import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_IN_COLLAPSIBLE_ID
-import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_IN_RIGID_ID
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_IN_STATEFUL_ID
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_IN_STATELESS_ID
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_OTHER_PACKAGE_ID
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.HIDDEN_ONLY_CONFIG
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_ALL_OPTIONAL_ID
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_BAREBONE_ID
-import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_IN_RIGID_ID
-import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MIXED_COLLAPSIBLE_GROUP_ID
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_IN_STATELESS_ID
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_SOURCE_CONFIG
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_SOURCE_NO_GROUP_TITLE_CONFIG
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MIXED_STATEFUL_GROUP_ID
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MULTIPLE_SOURCES_CONFIG
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.NO_PAGE_OPEN_CONFIG
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.PACKAGE_CERT_HASH_INVALID
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SAMPLE_SOURCE_ID
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SEVERITY_ZERO_CONFIG
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_CONFIG
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_ID
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_INVALID_INTENT_CONFIG
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_OTHER_PACKAGE_CONFIG
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_WITH_FAKE_CERT
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_WITH_INVALID_CERT
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_1
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_2
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_3
@@ -106,11 +117,14 @@
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_6
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_7
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.STATIC_BAREBONE_ID
-import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.STATIC_IN_COLLAPSIBLE_ID
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.STATIC_IN_STATEFUL_ID
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SUMMARY_TEST_CONFIG
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SUMMARY_TEST_GROUP_ID
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.getLockScreenSourceConfig
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.multipleSourcesWithDeduplicationInfoConfig
 import android.safetycenter.cts.testing.SafetyCenterCtsData
+import android.safetycenter.cts.testing.SafetyCenterCtsData.Companion.withAttributionTitleInIssuesIfAtLeastU
+import android.safetycenter.cts.testing.SafetyCenterCtsData.Companion.withDismissedIssuesIfAtLeastU
 import android.safetycenter.cts.testing.SafetyCenterCtsHelper
 import android.safetycenter.cts.testing.SafetyCenterCtsListener
 import android.safetycenter.cts.testing.SafetyCenterEnabledChangedReceiver
@@ -131,6 +145,7 @@
 import android.safetycenter.cts.testing.SafetySourceReceiver.Companion.refreshSafetySourcesWithoutReceiverPermissionAndWait
 import androidx.test.core.app.ApplicationProvider.getApplicationContext
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
 import com.android.compatibility.common.preconditions.ScreenLockHelper
 import com.android.safetycenter.resources.SafetyCenterResourcesContext
 import com.google.common.base.Preconditions.checkState
@@ -140,6 +155,7 @@
 import kotlin.test.assertFailsWith
 import kotlinx.coroutines.TimeoutCancellationException
 import org.junit.After
+import org.junit.Assume.assumeFalse
 import org.junit.Assume.assumeTrue
 import org.junit.Before
 import org.junit.Test
@@ -275,15 +291,14 @@
 
     private val safetyCenterEntryGroupMixedFromComplexConfig =
         SafetyCenterEntryOrGroup(
-            SafetyCenterEntryGroup.Builder(
-                    SafetyCenterCtsData.entryGroupId(MIXED_COLLAPSIBLE_GROUP_ID), "OK")
+            SafetyCenterEntryGroup.Builder(MIXED_STATEFUL_GROUP_ID, "OK")
                 .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN)
                 .setSummary(safetyCenterResourcesContext.getStringByName("group_unknown_summary"))
                 .setEntries(
                     listOf(
-                        safetyCenterCtsData.safetyCenterEntryDefault(DYNAMIC_IN_COLLAPSIBLE_ID),
+                        safetyCenterCtsData.safetyCenterEntryDefault(DYNAMIC_IN_STATEFUL_ID),
                         SafetyCenterEntry.Builder(
-                                SafetyCenterCtsData.entryId(STATIC_IN_COLLAPSIBLE_ID), "OK")
+                                SafetyCenterCtsData.entryId(STATIC_IN_STATEFUL_ID), "OK")
                             .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
                             .setSummary("OK")
                             .setPendingIntent(safetySourceCtsData.testActivityRedirectPendingIntent)
@@ -374,7 +389,7 @@
                     safetyCenterCtsData
                         .safetyCenterEntryOkBuilder(SINGLE_SOURCE_ID)
                         .setIconAction(
-                            ICON_ACTION_TYPE_GEAR,
+                            ICON_ACTION_TYPE_INFO,
                             safetySourceCtsData.testActivityRedirectPendingIntent)
                         .build())),
             emptyList())
@@ -481,8 +496,7 @@
             emptyList(),
             listOf(
                 SafetyCenterEntryOrGroup(
-                    SafetyCenterEntryGroup.Builder(
-                            SafetyCenterCtsData.entryGroupId(DYNAMIC_GROUP_ID), "OK")
+                    SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
                         .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN)
                         .setSummary(
                             safetyCenterResourcesContext.getStringByName("group_unknown_summary"))
@@ -518,12 +532,11 @@
                 safetyCenterCtsData.safetyCenterIssueCritical(ISSUE_ONLY_BAREBONE_ID),
                 safetyCenterCtsData.safetyCenterIssueRecommendation(DYNAMIC_DISABLED_ID),
                 safetyCenterCtsData.safetyCenterIssueRecommendation(ISSUE_ONLY_ALL_OPTIONAL_ID),
-                safetyCenterCtsData.safetyCenterIssueInformation(DYNAMIC_IN_RIGID_ID),
-                safetyCenterCtsData.safetyCenterIssueInformation(ISSUE_ONLY_IN_RIGID_ID)),
+                safetyCenterCtsData.safetyCenterIssueInformation(DYNAMIC_IN_STATELESS_ID),
+                safetyCenterCtsData.safetyCenterIssueInformation(ISSUE_ONLY_IN_STATELESS_ID)),
             listOf(
                 SafetyCenterEntryOrGroup(
-                    SafetyCenterEntryGroup.Builder(
-                            SafetyCenterCtsData.entryGroupId(DYNAMIC_GROUP_ID), "OK")
+                    SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
                         .setSeverityLevel(ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING)
                         .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_PRIVACY)
                         .setSummary("Critical summary")
@@ -635,14 +648,14 @@
     }
 
     @Test
-    fun setSafetySourceData_sourceInRigidGroupUnspecified_setsValue() {
+    fun setSafetySourceData_sourceInStatelessGroupUnspecified_setsValue() {
         safetyCenterCtsHelper.setConfig(COMPLEX_CONFIG)
 
         val dataToSet = safetySourceCtsData.unspecified
-        safetyCenterCtsHelper.setData(DYNAMIC_IN_RIGID_ID, dataToSet)
+        safetyCenterCtsHelper.setData(DYNAMIC_IN_STATELESS_ID, dataToSet)
 
         val apiSafetySourceData =
-            safetyCenterManager.getSafetySourceDataWithPermission(DYNAMIC_IN_RIGID_ID)
+            safetyCenterManager.getSafetySourceDataWithPermission(DYNAMIC_IN_STATELESS_ID)
         assertThat(apiSafetySourceData).isEqualTo(dataToSet)
     }
 
@@ -688,19 +701,50 @@
     }
 
     @Test
-    fun setSafetySourceData_sourceInRigidGroupNotUnspecified_throwsIllegalArgumentException() {
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun setSafetySourceData_wronglySignedPackage_throwsIllegalArgumentException() {
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_WITH_FAKE_CERT)
+
+        val thrown =
+            assertFailsWith(IllegalArgumentException::class) {
+                safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.unspecified)
+            }
+
+        assertThat(thrown)
+            .hasMessageThat()
+            .isEqualTo("Invalid signature for package " + context.packageName)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun setSafetySourceData_invalidPackageCertificate_throwsIllegalArgumentException() {
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_WITH_INVALID_CERT)
+
+        val thrown =
+            assertFailsWith(IllegalStateException::class) {
+                safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.unspecified)
+            }
+
+        assertThat(thrown)
+            .hasMessageThat()
+            .isEqualTo("Failed to parse signing certificate: " + PACKAGE_CERT_HASH_INVALID)
+    }
+
+    @Test
+    fun setSafetySourceData_sourceInStatelessGroupNotUnspecified_throwsIllegalArgumentException() {
         safetyCenterCtsHelper.setConfig(COMPLEX_CONFIG)
 
         val thrown =
             assertFailsWith(IllegalArgumentException::class) {
-                safetyCenterCtsHelper.setData(DYNAMIC_IN_RIGID_ID, safetySourceCtsData.information)
+                safetyCenterCtsHelper.setData(
+                    DYNAMIC_IN_STATELESS_ID, safetySourceCtsData.information)
             }
 
         assertThat(thrown)
             .hasMessageThat()
             .isEqualTo(
-                "Safety source: $DYNAMIC_IN_RIGID_ID is in a rigid group but specified a severity" +
-                    " level: ${SafetySourceData.SEVERITY_LEVEL_INFORMATION}")
+                "Safety source: $DYNAMIC_IN_STATELESS_ID is in a stateless group but specified a " +
+                    "severity level: $SEVERITY_LEVEL_INFORMATION")
     }
 
     @Test
@@ -1284,6 +1328,21 @@
     }
 
     @Test
+    fun refreshSafetySources_withRefreshReasonPageOpen_calledIfSourceHasADeviceConfigOverride() {
+        SafetyCenterFlags.overrideRefreshOnPageOpenSources = setOf(SINGLE_SOURCE_ID)
+        safetyCenterCtsHelper.setConfig(NO_PAGE_OPEN_CONFIG)
+        SafetySourceReceiver.setResponse(
+            Request.Refresh(SINGLE_SOURCE_ID), Response.SetData(safetySourceCtsData.information))
+
+        safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
+            REFRESH_REASON_PAGE_OPEN)
+
+        val apiSafetySourceData =
+            safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
+        assertThat(apiSafetySourceData).isEqualTo(safetySourceCtsData.information)
+    }
+
+    @Test
     fun refreshSafetySources_whenSourceClearsData_sourceSendsNullData() {
         safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
         safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.information)
@@ -1782,6 +1841,109 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun refreshSafetySources_withRefreshReasonPeriodic_noBackgroundRefreshSourceDoesNotSendData() {
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+        SafetySourceReceiver.setResponse(
+            Request.Refresh(SINGLE_SOURCE_ID), Response.SetData(safetySourceCtsData.information))
+        SafetyCenterFlags.backgroundRefreshDeniedSources = setOf(SINGLE_SOURCE_ID)
+
+        assertFailsWith(TimeoutCancellationException::class) {
+            safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
+                REFRESH_REASON_PERIODIC, TIMEOUT_SHORT)
+        }
+
+        val apiSafetySourceData =
+            safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
+        assertThat(apiSafetySourceData).isNull()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun refreshSafetySources_withRefreshReasonPeriodic_backgroundRefreshSourceSendsData() {
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+        SafetySourceReceiver.setResponse(
+            Request.Refresh(SINGLE_SOURCE_ID),
+            Response.SetData(safetySourceCtsData.criticalWithResolvingGeneralIssue))
+
+        safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
+            REFRESH_REASON_PERIODIC)
+
+        val sourceData = safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
+        assertThat(sourceData).isEqualTo(safetySourceCtsData.criticalWithResolvingGeneralIssue)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun refreshSafetySources_withSafetySourceIds_onlySpecifiedSourcesSendData() {
+        safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
+        SafetySourceReceiver.apply {
+            setResponse(
+                Request.Refresh(SOURCE_ID_1), Response.SetData(safetySourceCtsData.information))
+            setResponse(
+                Request.Refresh(SOURCE_ID_2), Response.SetData(safetySourceCtsData.information))
+            setResponse(
+                Request.Refresh(SOURCE_ID_3), Response.SetData(safetySourceCtsData.information))
+        }
+
+        safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
+            REFRESH_REASON_PAGE_OPEN, safetySourceIds = listOf(SOURCE_ID_1, SOURCE_ID_2))
+
+        val apiSafetySourceData1 =
+            safetyCenterManager.getSafetySourceDataWithPermission(SOURCE_ID_1)
+        assertThat(apiSafetySourceData1).isEqualTo(safetySourceCtsData.information)
+        val apiSafetySourceData2 =
+            safetyCenterManager.getSafetySourceDataWithPermission(SOURCE_ID_2)
+        assertThat(apiSafetySourceData2).isEqualTo(safetySourceCtsData.information)
+        val apiSafetySourceData3 =
+            safetyCenterManager.getSafetySourceDataWithPermission(SOURCE_ID_3)
+        assertThat(apiSafetySourceData3).isNull()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun refreshSafetySources_withEmptySafetySourceIds_NoSourcesSendData() {
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+        SafetySourceReceiver.setResponse(
+            Request.Refresh(SINGLE_SOURCE_ID),
+            Response.SetData(safetySourceCtsData.criticalWithResolvingGeneralIssue))
+
+        assertFailsWith(TimeoutCancellationException::class) {
+            safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
+                REFRESH_REASON_PAGE_OPEN, TIMEOUT_SHORT, emptyList())
+        }
+
+        val sourceData = safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
+        assertThat(sourceData).isNull()
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = TIRAMISU)
+    fun refreshSafetySources_versionLessThanU_throwsUnsupportedOperationException() {
+        // TODO(b/258228790): Remove after U is no longer in pre-release
+        assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
+        safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
+
+        val exception =
+            assertFailsWith(UnsupportedOperationException::class) {
+                safetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
+                    REFRESH_REASON_PAGE_OPEN, safetySourceIds = listOf(SOURCE_ID_1, SOURCE_ID_3))
+            }
+
+        assertThat(exception)
+            .hasMessageThat()
+            .isEqualTo("Method not supported for versions lower than UPSIDE_DOWN_CAKE")
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun refreshSafetySources_withSafetySourceIds_withoutPermission_throwsSecurityException() {
+        assertFailsWith(SecurityException::class) {
+            safetyCenterManager.refreshSafetySources(REFRESH_REASON_PAGE_OPEN, listOf())
+        }
+    }
+
+    @Test
     fun refreshSafetySources_withoutPermission_throwsSecurityException() {
         assertFailsWith(SecurityException::class) {
             safetyCenterManager.refreshSafetySources(REFRESH_REASON_RESCAN_BUTTON_CLICK)
@@ -1828,6 +1990,17 @@
     }
 
     @Test
+    fun getSafetyCenterData_withOnlyHiddenSourcesWithoutDataProvided_returnsNoGroups() {
+        safetyCenterCtsHelper.setConfig(HIDDEN_ONLY_CONFIG)
+
+        val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+
+        assertThat(apiSafetyCenterData)
+            .isEqualTo(
+                SafetyCenterData(safetyCenterStatusOk, emptyList(), emptyList(), emptyList()))
+    }
+
+    @Test
     fun getSafetyCenterData_withSomeDataProvided_returnsDataProvided() {
         safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
         safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.unspecified)
@@ -1849,11 +2022,71 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_attributionTitleProvidedBySource_returnsIssueWithAttributionTitle() {
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+        safetyCenterCtsHelper.setData(
+            SINGLE_SOURCE_ID, safetySourceCtsData.informationWithIssueWithAttributionTitle)
+
+        val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+
+        val expectedSafetyCenterData =
+            safetyCenterDataOkOneAlert.withAttributionTitleInIssuesIfAtLeastU("Attribution Title")
+        assertThat(apiSafetyCenterData).isEqualTo(expectedSafetyCenterData)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_attributionTitleNotProvided_returnsGroupTitleAsAttributionTitle() {
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+        safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.informationWithIssue)
+
+        val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+
+        val expectedSafetyCenterData =
+            safetyCenterDataOkOneAlert.withAttributionTitleInIssuesIfAtLeastU("OK")
+        assertThat(apiSafetyCenterData).isEqualTo(expectedSafetyCenterData)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_attributionNotSetAndGroupTitleNull_returnsNullAttributionTitle() {
+        safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_NO_GROUP_TITLE_CONFIG)
+        safetyCenterCtsHelper.setData(
+            ISSUE_ONLY_ALL_OPTIONAL_ID,
+            SafetySourceCtsData.issuesOnly(safetySourceCtsData.recommendationGeneralIssue))
+
+        val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+
+        val expectedSafetyCenterData =
+            SafetyCenterData(
+                safetyCenterStatusGeneralRecommendationOneAlert,
+                listOf(
+                    safetyCenterCtsData.safetyCenterIssueRecommendation(
+                        ISSUE_ONLY_ALL_OPTIONAL_ID, attributionTitle = null)),
+                emptyList(),
+                emptyList())
+        assertThat(apiSafetyCenterData).isEqualTo(expectedSafetyCenterData)
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = TIRAMISU)
+    fun getSafetyCenterData_attributionNotSetBySourceOnTiramisu_returnsNullAttributionTitle() {
+        // TODO(b/258228790): Remove after U is no longer in pre-release
+        assumeFalse(CODENAME == "UpsideDownCake")
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+        safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.informationWithIssue)
+
+        val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+
+        assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataOkOneAlert)
+    }
+
+    @Test
     fun getSafetyCenterData_withUpdatedData_returnsUpdatedData() {
         safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
         safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.information)
-        val previousApiSafetyCenterData =
-            safetyCenterManager.getSafetySourceDataWithPermission(SINGLE_SOURCE_ID)
+        val previousApiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
         safetyCenterCtsHelper.setData(
             SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
 
@@ -1951,6 +2184,132 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_withRecommendationDataIssue_returnsDataRecommendationStatus() {
+        safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_CONFIG)
+        safetyCenterCtsHelper.setData(
+            ISSUE_ONLY_ALL_OPTIONAL_ID,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData
+                    .defaultRecommendationIssueBuilder()
+                    .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA)
+                    .build()))
+
+        val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
+
+        assertThat(apiSafetyCenterStatus)
+            .isEqualTo(
+                safetyCenterCtsData.safetyCenterStatusOneAlert(
+                    "overall_severity_level_data_recommendation_title",
+                    OVERALL_SEVERITY_LEVEL_RECOMMENDATION))
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_withCriticalDataIssue_returnsDataCriticalStatus() {
+        safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_CONFIG)
+        safetyCenterCtsHelper.setData(
+            ISSUE_ONLY_ALL_OPTIONAL_ID,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData
+                    .defaultCriticalResolvingIssueBuilder()
+                    .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA)
+                    .build()))
+
+        val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
+
+        assertThat(apiSafetyCenterStatus)
+            .isEqualTo(
+                safetyCenterCtsData.safetyCenterStatusOneAlert(
+                    "overall_severity_level_critical_data_warning_title",
+                    OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING))
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_withRecommendationPasswordsIssue_returnsDataRecommendationStatus() {
+        safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_CONFIG)
+        safetyCenterCtsHelper.setData(
+            ISSUE_ONLY_ALL_OPTIONAL_ID,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData
+                    .defaultRecommendationIssueBuilder()
+                    .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS)
+                    .build()))
+
+        val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
+
+        assertThat(apiSafetyCenterStatus)
+            .isEqualTo(
+                safetyCenterCtsData.safetyCenterStatusOneAlert(
+                    "overall_severity_level_passwords_recommendation_title",
+                    OVERALL_SEVERITY_LEVEL_RECOMMENDATION))
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_withCriticalPasswordsIssue_returnsDataCriticalStatus() {
+        safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_CONFIG)
+        safetyCenterCtsHelper.setData(
+            ISSUE_ONLY_ALL_OPTIONAL_ID,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData
+                    .defaultCriticalResolvingIssueBuilder()
+                    .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS)
+                    .build()))
+
+        val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
+
+        assertThat(apiSafetyCenterStatus)
+            .isEqualTo(
+                safetyCenterCtsData.safetyCenterStatusOneAlert(
+                    "overall_severity_level_critical_passwords_warning_title",
+                    OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING))
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_withRecommendationPersonalIssue_returnsDataRecommendationStatus() {
+        safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_CONFIG)
+        safetyCenterCtsHelper.setData(
+            ISSUE_ONLY_ALL_OPTIONAL_ID,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData
+                    .defaultRecommendationIssueBuilder()
+                    .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PERSONAL_SAFETY)
+                    .build()))
+
+        val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
+
+        assertThat(apiSafetyCenterStatus)
+            .isEqualTo(
+                safetyCenterCtsData.safetyCenterStatusOneAlert(
+                    "overall_severity_level_personal_recommendation_title",
+                    OVERALL_SEVERITY_LEVEL_RECOMMENDATION))
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_withCriticalPersonalIssue_returnsDataCriticalStatus() {
+        safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_CONFIG)
+        safetyCenterCtsHelper.setData(
+            ISSUE_ONLY_ALL_OPTIONAL_ID,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData
+                    .defaultCriticalResolvingIssueBuilder()
+                    .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PERSONAL_SAFETY)
+                    .build()))
+
+        val apiSafetyCenterStatus = safetyCenterManager.getSafetyCenterDataWithPermission().status
+
+        assertThat(apiSafetyCenterStatus)
+            .isEqualTo(
+                safetyCenterCtsData.safetyCenterStatusOneAlert(
+                    "overall_severity_level_critical_personal_warning_title",
+                    OVERALL_SEVERITY_LEVEL_CRITICAL_WARNING))
+    }
+
+    @Test
     fun getSafetyCenterData_singleSourceIssues_returnsOverallStatusBasedOnHigherSeverityIssue() {
         safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
         safetyCenterCtsHelper.setData(
@@ -1989,6 +2348,447 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_duplicateIssuesOfSameSeverities_issueOfFirstSourceInConfigShown() {
+        safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig)
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_1,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_5,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+
+        val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues
+
+        assertThat(apiSafetyCenterIssues)
+            .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1))
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_differentDuplicationId_bothIssuesShown() {
+        safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig)
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_1,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_5,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("different")))
+
+        val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues
+
+        assertThat(apiSafetyCenterIssues)
+            .containsExactly(
+                safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1),
+                safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_5))
+            .inOrder()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_differentDuplicationGroup_bothIssuesShown() {
+        safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig)
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_1,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+        // Belongs to DEDUPLICATION_GROUP_3
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_6,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+
+        val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues
+
+        assertThat(apiSafetyCenterIssues)
+            .containsExactly(
+                safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1),
+                safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_6))
+            .inOrder()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_threeDuplicateIssues_onlyOneIssueShown() {
+        safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig)
+        // Belongs to DEDUPLICATION_GROUP_3
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_4,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+        // Belongs to DEDUPLICATION_GROUP_3
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_6,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+        // Belongs to DEDUPLICATION_GROUP_3
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_7,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+
+        val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues
+
+        assertThat(apiSafetyCenterIssues)
+            .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_4))
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_duplicateIssuesOfDifferentSeverities_moreSevereIssueShown() {
+        safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig)
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_2,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.recommendationIssueWithDeduplicationId("same")))
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_5,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+
+        val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues
+
+        assertThat(apiSafetyCenterIssues)
+            .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_5))
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_multipleDuplicationsOfIssues_correctlyDeduplicated() {
+        safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig)
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_1,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.recommendationIssueWithDeduplicationId("A")))
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_2,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.recommendationIssueWithDeduplicationId("A")))
+        // Belongs to DEDUPLICATION_GROUP_2
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_3,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.recommendationIssueWithDeduplicationId("B")))
+        // Belongs to DEDUPLICATION_GROUP_3
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_4,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.recommendationIssueWithDeduplicationId("B")))
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_5,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("A")))
+        // Belongs to DEDUPLICATION_GROUP_3
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_6,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.recommendationIssueWithDeduplicationId("B")))
+        // Belongs to DEDUPLICATION_GROUP_3
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_7,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.recommendationIssueWithDeduplicationId("B")))
+
+        val apiSafetyCenterIssues = safetyCenterManager.getSafetyCenterDataWithPermission().issues
+
+        assertThat(apiSafetyCenterIssues)
+            .containsExactly(
+                safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_5),
+                safetyCenterCtsData.safetyCenterIssueRecommendation(SOURCE_ID_3),
+                safetyCenterCtsData.safetyCenterIssueRecommendation(SOURCE_ID_4))
+            .inOrder()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_duplicateIssuesBothDismissed_topOneShownAsDismissed() {
+        safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig)
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_1,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_5,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.recommendationIssueWithDeduplicationId("same")))
+        safetyCenterManager.dismissSafetyCenterIssueWithPermission(
+            SafetyCenterCtsData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID))
+        safetyCenterManager.dismissSafetyCenterIssueWithPermission(
+            SafetyCenterCtsData.issueId(SOURCE_ID_5, RECOMMENDATION_ISSUE_ID))
+
+        val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+        val apiSafetyCenterIssues = apiSafetyCenterData.issues
+        val apiSafetyCenterDismissedIssues = apiSafetyCenterData.dismissedIssues
+
+        assertThat(apiSafetyCenterIssues).isEmpty()
+        assertThat(apiSafetyCenterDismissedIssues)
+            .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1))
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_duplicateIssuesLowerSeverityOneDismissed_topOneShown() {
+        safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig)
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_1,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_5,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.recommendationIssueWithDeduplicationId("same")))
+        safetyCenterManager.dismissSafetyCenterIssueWithPermission(
+            SafetyCenterCtsData.issueId(SOURCE_ID_5, RECOMMENDATION_ISSUE_ID))
+
+        val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+        val apiSafetyCenterIssues = apiSafetyCenterData.issues
+        val apiSafetyCenterDismissedIssues = apiSafetyCenterData.dismissedIssues
+
+        assertThat(apiSafetyCenterIssues)
+            .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1))
+        assertThat(apiSafetyCenterDismissedIssues).isEmpty()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_duplicateIssuesHigherSeverityOneDismissed_topOneShownAsDismissed() {
+        safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig)
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_1,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_5,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.recommendationIssueWithDeduplicationId("same")))
+        safetyCenterManager.dismissSafetyCenterIssueWithPermission(
+            SafetyCenterCtsData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID))
+
+        val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+        val apiSafetyCenterIssues = apiSafetyCenterData.issues
+        val apiSafetyCenterDismissedIssues = apiSafetyCenterData.dismissedIssues
+
+        assertThat(apiSafetyCenterIssues).isEmpty()
+        assertThat(apiSafetyCenterDismissedIssues)
+            .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1))
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_dupIssuesLowerPrioritySameSeverityOneDismissed_topShownAsDismissed() {
+        safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig)
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_1,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_5,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+        safetyCenterManager.dismissSafetyCenterIssueWithPermission(
+            SafetyCenterCtsData.issueId(SOURCE_ID_5, CRITICAL_ISSUE_ID))
+
+        val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+        val apiSafetyCenterIssues = apiSafetyCenterData.issues
+        val apiSafetyCenterDismissedIssues = apiSafetyCenterData.dismissedIssues
+
+        assertThat(apiSafetyCenterIssues).isEmpty()
+        assertThat(apiSafetyCenterDismissedIssues)
+            .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1))
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_dupIssuesTopOneDismissedThenDisappears_bottomOneReemergesTimely() {
+        SafetyCenterFlags.resurfaceIssueMaxCounts = mapOf(SEVERITY_LEVEL_CRITICAL_WARNING to 99L)
+        SafetyCenterFlags.resurfaceIssueDelays =
+            mapOf(SEVERITY_LEVEL_CRITICAL_WARNING to RESURFACE_DELAY)
+        safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig)
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_1,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_5,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+        safetyCenterManager.dismissSafetyCenterIssueWithPermission(
+            SafetyCenterCtsData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID))
+        safetyCenterManager.getSafetyCenterDataWithPermission() // data used, 2nd issue dismissed
+        safetyCenterCtsHelper.setData(SOURCE_ID_1, SafetySourceCtsData.issuesOnly())
+
+        val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+        val apiSafetyCenterDismissedIssues = apiSafetyCenterData.dismissedIssues
+
+        assertThat(apiSafetyCenterDismissedIssues)
+            .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_5))
+        waitForWithTimeout(timeout = RESURFACE_TIMEOUT, checkPeriod = RESURFACE_CHECK) {
+            val hasResurfaced =
+                safetyCenterManager
+                    .getSafetyCenterDataWithPermission()
+                    .issues
+                    .contains(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_5))
+            hasResurfaced
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_dupsOfDiffSeveritiesTopOneDismissedThenGone_bottomOneReemergesTimely() {
+        SafetyCenterFlags.resurfaceIssueMaxCounts =
+            mapOf(
+                SEVERITY_LEVEL_INFORMATION to 0L,
+                SEVERITY_LEVEL_RECOMMENDATION to 99L,
+                SEVERITY_LEVEL_CRITICAL_WARNING to 0L)
+        SafetyCenterFlags.resurfaceIssueDelays =
+            mapOf(
+                SEVERITY_LEVEL_INFORMATION to Duration.ZERO,
+                SEVERITY_LEVEL_RECOMMENDATION to RESURFACE_DELAY,
+                SEVERITY_LEVEL_CRITICAL_WARNING to Duration.ZERO)
+        safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig)
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_1,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_5,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.recommendationIssueWithDeduplicationId("same")))
+        safetyCenterManager.dismissSafetyCenterIssueWithPermission(
+            SafetyCenterCtsData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID))
+        safetyCenterManager.getSafetyCenterDataWithPermission() // data used, 2nd issue dismissed
+        safetyCenterCtsHelper.setData(SOURCE_ID_1, SafetySourceCtsData.issuesOnly())
+
+        val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+        val apiSafetyCenterDismissedIssues = apiSafetyCenterData.dismissedIssues
+
+        assertThat(apiSafetyCenterDismissedIssues)
+            .containsExactly(safetyCenterCtsData.safetyCenterIssueRecommendation(SOURCE_ID_5))
+        waitForWithTimeout(timeout = RESURFACE_TIMEOUT, checkPeriod = RESURFACE_CHECK) {
+            val hasResurfaced =
+                safetyCenterManager
+                    .getSafetyCenterDataWithPermission()
+                    .issues
+                    .contains(safetyCenterCtsData.safetyCenterIssueRecommendation(SOURCE_ID_5))
+            hasResurfaced
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_duplicateIssuesLowerOneResurfaces_lowerOneStillFilteredOut() {
+        SafetyCenterFlags.resurfaceIssueMaxCounts =
+            mapOf(
+                SEVERITY_LEVEL_INFORMATION to 0L,
+                SEVERITY_LEVEL_RECOMMENDATION to 99L,
+                SEVERITY_LEVEL_CRITICAL_WARNING to 99L)
+        SafetyCenterFlags.resurfaceIssueDelays =
+            mapOf(
+                SEVERITY_LEVEL_INFORMATION to Duration.ZERO,
+                SEVERITY_LEVEL_RECOMMENDATION to RESURFACE_DELAY,
+                SEVERITY_LEVEL_CRITICAL_WARNING to RESURFACE_DELAY.multipliedBy(100))
+        safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig)
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_1,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_5,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.recommendationIssueWithDeduplicationId("same")))
+        safetyCenterManager.dismissSafetyCenterIssueWithPermission(
+            SafetyCenterCtsData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID))
+
+        // data used, 2nd issue dismissed
+        val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+        val apiSafetyCenterDismissedIssues = apiSafetyCenterData.dismissedIssues
+
+        assertThat(apiSafetyCenterDismissedIssues)
+            .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1))
+        assertFailsWith(TimeoutCancellationException::class) {
+            waitForWithTimeout(timeout = RESURFACE_TIMEOUT, checkPeriod = RESURFACE_CHECK) {
+                val hasResurfaced =
+                    safetyCenterManager
+                        .getSafetyCenterDataWithPermission()
+                        .issues
+                        .contains(safetyCenterCtsData.safetyCenterIssueRecommendation(SOURCE_ID_5))
+                hasResurfaced
+            }
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getSafetyCenterData_duplicateIssuesTopOneResurfaces_topOneShown() {
+        SafetyCenterFlags.resurfaceIssueMaxCounts =
+            mapOf(
+                SEVERITY_LEVEL_INFORMATION to 0L,
+                SEVERITY_LEVEL_RECOMMENDATION to 99L,
+                SEVERITY_LEVEL_CRITICAL_WARNING to 99L)
+        SafetyCenterFlags.resurfaceIssueDelays =
+            mapOf(
+                SEVERITY_LEVEL_INFORMATION to Duration.ZERO,
+                SEVERITY_LEVEL_RECOMMENDATION to RESURFACE_DELAY.multipliedBy(100),
+                SEVERITY_LEVEL_CRITICAL_WARNING to RESURFACE_DELAY)
+        safetyCenterCtsHelper.setConfig(multipleSourcesWithDeduplicationInfoConfig)
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_1,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.criticalIssueWithDeduplicationId("same")))
+        // Belongs to DEDUPLICATION_GROUP_1
+        safetyCenterCtsHelper.setData(
+            SOURCE_ID_5,
+            SafetySourceCtsData.issuesOnly(
+                safetySourceCtsData.recommendationIssueWithDeduplicationId("same")))
+        safetyCenterManager.dismissSafetyCenterIssueWithPermission(
+            SafetyCenterCtsData.issueId(SOURCE_ID_1, CRITICAL_ISSUE_ID))
+
+        // data used, 2nd issue dismissed
+        val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+        val apiSafetyCenterDismissedIssues = apiSafetyCenterData.dismissedIssues
+
+        assertThat(apiSafetyCenterDismissedIssues)
+            .containsExactly(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1))
+        waitForWithTimeout(timeout = RESURFACE_TIMEOUT, checkPeriod = RESURFACE_CHECK) {
+            val hasResurfaced =
+                safetyCenterManager
+                    .getSafetyCenterDataWithPermission()
+                    .issues
+                    .contains(safetyCenterCtsData.safetyCenterIssueCritical(SOURCE_ID_1))
+            hasResurfaced
+        }
+    }
+
+    @Test
     fun getSafetyCenterData_criticalDeviceIssues_returnsOverallStatusBasedOnAddIssueCallOrder() {
         safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
         safetyCenterCtsHelper.setData(
@@ -2028,9 +2828,10 @@
         safetyCenterCtsHelper.setData(
             ISSUE_ONLY_ALL_OPTIONAL_ID,
             SafetySourceCtsData.issuesOnly(safetySourceCtsData.recommendationGeneralIssue))
-        safetyCenterCtsHelper.setData(DYNAMIC_IN_RIGID_ID, safetySourceCtsData.unspecifiedWithIssue)
         safetyCenterCtsHelper.setData(
-            ISSUE_ONLY_IN_RIGID_ID,
+            DYNAMIC_IN_STATELESS_ID, safetySourceCtsData.unspecifiedWithIssue)
+        safetyCenterCtsHelper.setData(
+            ISSUE_ONLY_IN_STATELESS_ID,
             SafetySourceCtsData.issuesOnly(safetySourceCtsData.informationIssue))
 
         val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
@@ -2070,7 +2871,7 @@
             SOURCE_ID_7,
             safetySourceCtsData.buildSafetySourceDataWithSummary(
                 severityLevel = SEVERITY_LEVEL_CRITICAL_WARNING, entrySummary = "critical 2"))
-        // STATIC_IN_COLLAPSIBLE_ID behaves like an UNSPECIFIED dynamic entry
+        // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry
 
         val group =
             safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID)
@@ -2108,7 +2909,7 @@
             safetySourceCtsData.buildSafetySourceDataWithSummary(
                 severityLevel = SEVERITY_LEVEL_RECOMMENDATION, entrySummary = "recommendation 2"))
         // SOURCE_ID_7 leave as an UNKNOWN dynamic entry
-        // STATIC_IN_COLLAPSIBLE_ID behaves like an UNSPECIFIED dynamic entry
+        // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry
 
         val group =
             safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID)
@@ -2154,7 +2955,7 @@
             SOURCE_ID_7,
             safetySourceCtsData.buildSafetySourceDataWithSummary(
                 severityLevel = SEVERITY_LEVEL_UNSPECIFIED, entrySummary = "unspecified 3"))
-        // STATIC_IN_COLLAPSIBLE_ID behaves like an UNSPECIFIED dynamic entry
+        // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry
 
         val group =
             safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID)
@@ -2197,7 +2998,7 @@
             SOURCE_ID_7,
             safetySourceCtsData.buildSafetySourceDataWithSummary(
                 severityLevel = SEVERITY_LEVEL_UNSPECIFIED, entrySummary = "unspecified 4"))
-        // STATIC_IN_COLLAPSIBLE_ID behaves like an UNSPECIFIED dynamic entry
+        // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry
 
         val group =
             safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID)
@@ -2239,7 +3040,7 @@
             SOURCE_ID_7,
             safetySourceCtsData.buildSafetySourceDataWithSummary(
                 severityLevel = SEVERITY_LEVEL_UNSPECIFIED, entrySummary = "unspecified 7"))
-        // STATIC_IN_COLLAPSIBLE_ID behaves like an UNSPECIFIED dynamic entry
+        // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry
 
         val group =
             safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID)
@@ -2271,7 +3072,7 @@
             safetySourceCtsData.buildSafetySourceDataWithSummary(
                 severityLevel = SEVERITY_LEVEL_INFORMATION, entrySummary = "information"))
         // SOURCE_ID_7 leave as an UNKNOWN dynamic entry
-        // STATIC_IN_COLLAPSIBLE_ID behaves like an UNSPECIFIED dynamic entry
+        // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry
 
         val group =
             safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID)
@@ -2302,7 +3103,7 @@
             SOURCE_ID_7,
             safetySourceCtsData.buildSafetySourceDataWithSummary(
                 severityLevel = SEVERITY_LEVEL_INFORMATION, entrySummary = "information"))
-        // STATIC_IN_COLLAPSIBLE_ID behaves like an UNSPECIFIED dynamic entry
+        // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry
 
         val group =
             safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID)
@@ -2337,7 +3138,7 @@
                 severityLevel = SEVERITY_LEVEL_UNSPECIFIED,
                 entrySummary = "unspecified with issue",
                 withIssue = true))
-        // STATIC_IN_COLLAPSIBLE_ID behaves like an UNSPECIFIED dynamic entry
+        // STATIC_IN_STATEFUL_ID behaves like an UNSPECIFIED dynamic entry
 
         val group =
             safetyCenterManager.getSafetyCenterDataWithPermission().getGroup(SUMMARY_TEST_GROUP_ID)
@@ -2645,7 +3446,10 @@
             SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID))
 
         val safetyCenterDataFromListener = listener.receiveSafetyCenterData()
-        assertThat(safetyCenterDataFromListener).isEqualTo(safetyCenterDataOkReviewCriticalEntry)
+        val expectedSafetyCenterData =
+            safetyCenterDataOkReviewCriticalEntry.withDismissedIssuesIfAtLeastU(
+                listOf(safetyCenterCtsData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)))
+        assertThat(safetyCenterDataFromListener).isEqualTo(expectedSafetyCenterData)
     }
 
     @Test
@@ -2679,13 +3483,15 @@
         safetyCenterCtsHelper.setData(
             SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
         val listener = safetyCenterCtsHelper.addListener()
-
         safetyCenterManager.dismissSafetyCenterIssueWithPermission(
             SafetyCenterCtsData.issueId(
                 SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID, issueTypeId = "some_other_issue_type_id"))
 
         val safetyCenterDataFromListener = listener.receiveSafetyCenterData()
-        assertThat(safetyCenterDataFromListener).isEqualTo(safetyCenterDataOkReviewCriticalEntry)
+        val expectedSafetyCenterData =
+            safetyCenterDataOkReviewCriticalEntry.withDismissedIssuesIfAtLeastU(
+                listOf(safetyCenterCtsData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)))
+        assertThat(safetyCenterDataFromListener).isEqualTo(expectedSafetyCenterData)
     }
 
     @Test
@@ -2702,8 +3508,10 @@
             SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, RECOMMENDATION_ISSUE_ID))
 
         val safetyCenterDataAfterDismissal = listener.receiveSafetyCenterData()
-        assertThat(safetyCenterDataAfterDismissal)
-            .isEqualTo(safetyCenterDataOkReviewRecommendationEntry)
+        val expectedSafetyCenterData =
+            safetyCenterDataOkReviewRecommendationEntry.withDismissedIssuesIfAtLeastU(
+                listOf(safetyCenterCtsData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)))
+        assertThat(safetyCenterDataAfterDismissal).isEqualTo(expectedSafetyCenterData)
         val safetyCenterDataAfterSourceHandledDismissal = listener.receiveSafetyCenterData()
         assertThat(safetyCenterDataAfterSourceHandledDismissal).isEqualTo(safetyCenterDataOk)
     }
@@ -2716,13 +3524,14 @@
         safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, recommendationDismissPendingIntentIssue)
         recommendationDismissPendingIntentIssue.issues.first().onDismissPendingIntent!!.cancel()
         val listener = safetyCenterCtsHelper.addListener()
-
         safetyCenterManager.dismissSafetyCenterIssueWithPermission(
             SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, RECOMMENDATION_ISSUE_ID))
 
         val safetyCenterDataFromListener = listener.receiveSafetyCenterData()
-        assertThat(safetyCenterDataFromListener)
-            .isEqualTo(safetyCenterDataOkReviewRecommendationEntry)
+        val exectedSafetyCenterData =
+            safetyCenterDataOkReviewRecommendationEntry.withDismissedIssuesIfAtLeastU(
+                listOf(safetyCenterCtsData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)))
+        assertThat(safetyCenterDataFromListener).isEqualTo(exectedSafetyCenterData)
         assertFailsWith(TimeoutCancellationException::class) {
             listener.receiveSafetyCenterErrorDetails(TIMEOUT_SHORT)
         }
@@ -2790,10 +3599,14 @@
         safetyCenterManager.dismissSafetyCenterIssueWithPermission(
             SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, INFORMATION_ISSUE_ID))
 
+        val expectedSafetyCenterData =
+            safetyCenterDataOk.withDismissedIssuesIfAtLeastU(
+                listOf(safetyCenterCtsData.safetyCenterIssueInformation(SINGLE_SOURCE_ID)))
         assertFailsWith(TimeoutCancellationException::class) {
             waitForWithTimeout(timeout = TIMEOUT_SHORT) {
                 val hasResurfaced =
-                    safetyCenterManager.getSafetyCenterDataWithPermission() != safetyCenterDataOk
+                    safetyCenterManager.getSafetyCenterDataWithPermission() !=
+                        expectedSafetyCenterData
                 hasResurfaced
             }
         }
@@ -2819,10 +3632,14 @@
         safetyCenterManager.dismissSafetyCenterIssueWithPermission(
             SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, INFORMATION_ISSUE_ID))
 
+        val expectedSafetyCenterData =
+            safetyCenterDataOk.withDismissedIssuesIfAtLeastU(
+                listOf(safetyCenterCtsData.safetyCenterIssueInformation(SINGLE_SOURCE_ID)))
         assertFailsWith(TimeoutCancellationException::class) {
             waitForWithTimeout(timeout = TIMEOUT_SHORT) {
                 val hasResurfaced =
-                    safetyCenterManager.getSafetyCenterDataWithPermission() != safetyCenterDataOk
+                    safetyCenterManager.getSafetyCenterDataWithPermission() !=
+                        expectedSafetyCenterData
                 hasResurfaced
             }
         }
@@ -2858,11 +3675,15 @@
         safetyCenterManager.dismissSafetyCenterIssueWithPermission(
             SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, CRITICAL_ISSUE_ID))
 
+        val expectedSafetyCenterData =
+            safetyCenterDataOkReviewCriticalEntry.withDismissedIssuesIfAtLeastU(
+                listOf(safetyCenterCtsData.safetyCenterIssueCritical(SINGLE_SOURCE_ID)))
         assertFailsWith(TimeoutCancellationException::class) {
             waitForWithTimeout(timeout = TIMEOUT_SHORT) {
                 val hasResurfaced =
                     safetyCenterManager.getSafetyCenterDataWithPermission() !=
-                        safetyCenterDataOkReviewCriticalEntry
+                        expectedSafetyCenterData
+
                 hasResurfaced
             }
         }
@@ -2890,13 +3711,14 @@
         val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
         checkState(apiSafetyCenterData == safetyCenterDataDeviceRecommendationOneAlert)
         val listener = safetyCenterCtsHelper.addListener()
-
         safetyCenterManager.dismissSafetyCenterIssueWithPermission(
             SafetyCenterCtsData.issueId(SINGLE_SOURCE_ID, RECOMMENDATION_ISSUE_ID))
 
         val safetyCenterDataAfterDismissal = listener.receiveSafetyCenterData()
-        assertThat(safetyCenterDataAfterDismissal)
-            .isEqualTo(safetyCenterDataOkReviewRecommendationEntry)
+        val expectedSafetyCenterData =
+            safetyCenterDataOkReviewRecommendationEntry.withDismissedIssuesIfAtLeastU(
+                listOf(safetyCenterCtsData.safetyCenterIssueRecommendation(SINGLE_SOURCE_ID)))
+        assertThat(safetyCenterDataAfterDismissal).isEqualTo(expectedSafetyCenterData)
         waitForWithTimeout(timeout = RESURFACE_TIMEOUT, checkPeriod = RESURFACE_CHECK) {
             val hasResurfacedExactly =
                 safetyCenterManager.getSafetyCenterDataWithPermission() ==
@@ -3424,9 +4246,7 @@
     }
 
     private fun SafetyCenterData.getGroup(groupId: String): SafetyCenterEntryGroup =
-        entriesOrGroups
-            .first { it.entryGroup?.id == SafetyCenterCtsData.entryGroupId(groupId) }
-            .entryGroup!!
+        entriesOrGroups.first { it.entryGroup?.id == groupId }.entryGroup!!
 
     companion object {
         private val RESURFACE_DELAY = Duration.ofMillis(500)
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterMultiUsersTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterMultiUsersTest.kt
index 13e7e5b..71367f6 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterMultiUsersTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterMultiUsersTest.kt
@@ -43,11 +43,11 @@
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_DISABLED_ID
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_GROUP_ID
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_HIDDEN_ID
-import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_IN_RIGID_ID
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_IN_STATELESS_ID
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_ALL_OPTIONAL_ID
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_ALL_PROFILE_SOURCE_ID
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_BAREBONE_ID
-import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_IN_RIGID_ID
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_IN_STATELESS_ID
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_SOURCE_ALL_PROFILE_CONFIG
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_SOURCE_CONFIG
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_ALL_PROFILE_CONFIG
@@ -76,6 +76,8 @@
 import com.android.bedstead.harrier.annotations.Postsubmit
 import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner
 import com.android.bedstead.nene.types.OptionalBoolean.TRUE
+import com.android.compatibility.common.util.DisableAnimationRule
+import com.android.compatibility.common.util.FreezeRotationRule
 import com.android.safetycenter.resources.SafetyCenterResourcesContext
 import com.google.common.base.Preconditions.checkState
 import com.google.common.truth.Truth.assertThat
@@ -89,7 +91,11 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-/** CTS tests for our APIs and UI on a managed device (e.g. with managed profile(s)). */
+/**
+ * CTS tests for our APIs and UI on a device with multiple users. e.g. with a managed or secondary
+ * user(s).
+ */
+@Ignore // Tests are causing flakiness in other tests.
 @RunWith(BedsteadJUnit4::class)
 // TODO(b/234108780): Add these to presubmits when we figure a way to make sure they don't fail due
 // to timeouts with Bedstead.
@@ -99,6 +105,10 @@
         @JvmField @ClassRule @Rule val deviceState: DeviceState = DeviceState()
     }
 
+    @get:Rule val disableAnimationRule = DisableAnimationRule()
+
+    @get:Rule val freezeRotationRule = FreezeRotationRule()
+
     private val context: Context = ApplicationProvider.getApplicationContext()
     private val safetyCenterResourcesContext = SafetyCenterResourcesContext.forTests(context)
     private val safetyCenterCtsHelper = SafetyCenterCtsHelper(context)
@@ -114,11 +124,13 @@
     private val primaryProfileOnlyIssues =
         listOf(
             safetyCenterCtsData.safetyCenterIssueCritical(DYNAMIC_BAREBONE_ID),
-            safetyCenterCtsData.safetyCenterIssueCritical(ISSUE_ONLY_BAREBONE_ID),
+            safetyCenterCtsData.safetyCenterIssueCritical(
+                ISSUE_ONLY_BAREBONE_ID, attributionTitle = null),
             safetyCenterCtsData.safetyCenterIssueRecommendation(DYNAMIC_DISABLED_ID),
-            safetyCenterCtsData.safetyCenterIssueRecommendation(ISSUE_ONLY_ALL_OPTIONAL_ID),
-            safetyCenterCtsData.safetyCenterIssueInformation(DYNAMIC_IN_RIGID_ID),
-            safetyCenterCtsData.safetyCenterIssueInformation(ISSUE_ONLY_IN_RIGID_ID))
+            safetyCenterCtsData.safetyCenterIssueRecommendation(
+                ISSUE_ONLY_ALL_OPTIONAL_ID, attributionTitle = null),
+            safetyCenterCtsData.safetyCenterIssueInformation(DYNAMIC_IN_STATELESS_ID),
+            safetyCenterCtsData.safetyCenterIssueInformation(ISSUE_ONLY_IN_STATELESS_ID))
 
     private val dynamicBareboneDefault =
         safetyCenterCtsData.safetyCenterEntryDefault(DYNAMIC_BAREBONE_ID)
@@ -165,7 +177,7 @@
         get() = safetyCenterEntryOkForWork(DYNAMIC_HIDDEN_ID, deviceState.workProfile().id())
 
     private val staticGroupBuilder =
-        SafetyCenterEntryGroup.Builder(SafetyCenterCtsData.entryGroupId(STATIC_GROUP_ID), "OK")
+        SafetyCenterEntryGroup.Builder(STATIC_GROUP_ID, "OK")
             .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
             .setSeverityUnspecifiedIconType(SEVERITY_UNSPECIFIED_ICON_TYPE_PRIVACY)
             .setSummary("OK")
@@ -202,19 +214,19 @@
                 .setEnabled(false)
                 .build()
 
-    private val rigidEntry =
+    private val staticEntry =
         SafetyCenterStaticEntry.Builder("OK")
             .setSummary("OK")
             .setPendingIntent(safetySourceCtsData.testActivityRedirectPendingIntent)
             .build()
 
-    private val rigidEntryUpdated =
+    private val staticEntryUpdated =
         SafetyCenterStaticEntry.Builder("Unspecified title")
             .setSummary("Unspecified summary")
             .setPendingIntent(safetySourceCtsData.testActivityRedirectPendingIntent)
             .build()
 
-    private val rigidEntryForWorkBuilder
+    private val staticEntryForWorkBuilder
         get() =
             SafetyCenterStaticEntry.Builder("Paste")
                 .setSummary("OK")
@@ -222,18 +234,18 @@
                     createTestActivityRedirectPendingIntentForUser(
                         deviceState.workProfile().userHandle()))
 
-    private val rigidEntryForWork
-        get() = rigidEntryForWorkBuilder.build()
+    private val staticEntryForWork
+        get() = staticEntryForWorkBuilder.build()
 
-    private val rigidEntryForWorkPaused
+    private val staticEntryForWorkPaused
         get() =
-            rigidEntryForWorkBuilder
+            staticEntryForWorkBuilder
                 // TODO(b/233188021): This needs to use the Enterprise API to override the "work"
                 //  keyword.
                 .setSummary(safetyCenterResourcesContext.getStringByName("work_profile_paused"))
                 .build()
 
-    private val rigidEntryForWorkUpdated =
+    private val staticEntryForWorkUpdated =
         SafetyCenterStaticEntry.Builder("Unspecified title for Work")
             .setSummary("Unspecified summary")
             .setPendingIntent(safetySourceCtsData.testActivityRedirectPendingIntent)
@@ -382,8 +394,7 @@
                 emptyList(),
                 listOf(
                     SafetyCenterEntryOrGroup(
-                        SafetyCenterEntryGroup.Builder(
-                                SafetyCenterCtsData.entryGroupId(DYNAMIC_GROUP_ID), "OK")
+                        SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
                             .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN)
                             .setSummary(
                                 safetyCenterResourcesContext.getStringByName(
@@ -394,7 +405,7 @@
                         staticGroupBuilder
                             .setEntries(listOf(staticBarebone, staticAllOptional))
                             .build())),
-                listOf(SafetyCenterStaticEntryGroup("OK", listOf(rigidEntry, rigidEntry))))
+                listOf(SafetyCenterStaticEntryGroup("OK", listOf(staticEntry, staticEntry))))
         assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataFromComplexConfig)
     }
 
@@ -412,8 +423,7 @@
                 emptyList(),
                 listOf(
                     SafetyCenterEntryOrGroup(
-                        SafetyCenterEntryGroup.Builder(
-                                SafetyCenterCtsData.entryGroupId(DYNAMIC_GROUP_ID), "OK")
+                        SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
                             .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN)
                             .setSummary(
                                 safetyCenterResourcesContext.getStringByName(
@@ -432,7 +442,7 @@
                 listOf(
                     SafetyCenterStaticEntryGroup(
                         "OK",
-                        listOf(rigidEntry, rigidEntryForWork, rigidEntry, rigidEntryForWork))))
+                        listOf(staticEntry, staticEntryForWork, staticEntry, staticEntryForWork))))
         assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataFromComplexConfig)
     }
 
@@ -451,8 +461,7 @@
                 primaryProfileOnlyIssues,
                 listOf(
                     SafetyCenterEntryOrGroup(
-                        SafetyCenterEntryGroup.Builder(
-                                SafetyCenterCtsData.entryGroupId(DYNAMIC_GROUP_ID), "OK")
+                        SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
                             .setSeverityLevel(ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING)
                             .setSummary("Critical summary")
                             .setEntries(
@@ -471,7 +480,10 @@
                     SafetyCenterStaticEntryGroup(
                         "OK",
                         listOf(
-                            rigidEntryUpdated, rigidEntryForWork, rigidEntry, rigidEntryForWork))))
+                            staticEntryUpdated,
+                            staticEntryForWork,
+                            staticEntry,
+                            staticEntryForWork))))
         assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataFromComplexConfig)
     }
 
@@ -491,25 +503,26 @@
                 safetyCenterCtsData.safetyCenterStatusCritical(11),
                 listOf(
                     safetyCenterCtsData.safetyCenterIssueCritical(DYNAMIC_BAREBONE_ID),
-                    safetyCenterCtsData.safetyCenterIssueCritical(ISSUE_ONLY_BAREBONE_ID),
+                    safetyCenterCtsData.safetyCenterIssueCritical(
+                        ISSUE_ONLY_BAREBONE_ID, attributionTitle = null),
                     safetyCenterCtsData.safetyCenterIssueRecommendation(DYNAMIC_DISABLED_ID),
-                    safetyCenterCtsData.safetyCenterIssueRecommendation(ISSUE_ONLY_ALL_OPTIONAL_ID),
+                    safetyCenterCtsData.safetyCenterIssueRecommendation(
+                        ISSUE_ONLY_ALL_OPTIONAL_ID, attributionTitle = null),
                     safetyCenterCtsData.safetyCenterIssueInformation(
                         DYNAMIC_DISABLED_ID, managedUserId),
                     safetyCenterCtsData.safetyCenterIssueInformation(
                         DYNAMIC_HIDDEN_ID, managedUserId),
                     safetyCenterCtsData.safetyCenterIssueInformation(
-                        ISSUE_ONLY_ALL_OPTIONAL_ID, managedUserId),
-                    safetyCenterCtsData.safetyCenterIssueInformation(DYNAMIC_IN_RIGID_ID),
+                        ISSUE_ONLY_ALL_OPTIONAL_ID, managedUserId, attributionTitle = null),
+                    safetyCenterCtsData.safetyCenterIssueInformation(DYNAMIC_IN_STATELESS_ID),
                     safetyCenterCtsData.safetyCenterIssueInformation(
-                        DYNAMIC_IN_RIGID_ID, managedUserId),
-                    safetyCenterCtsData.safetyCenterIssueInformation(ISSUE_ONLY_IN_RIGID_ID),
+                        DYNAMIC_IN_STATELESS_ID, managedUserId),
+                    safetyCenterCtsData.safetyCenterIssueInformation(ISSUE_ONLY_IN_STATELESS_ID),
                     safetyCenterCtsData.safetyCenterIssueInformation(
-                        ISSUE_ONLY_IN_RIGID_ID, managedUserId)),
+                        ISSUE_ONLY_IN_STATELESS_ID, managedUserId)),
                 listOf(
                     SafetyCenterEntryOrGroup(
-                        SafetyCenterEntryGroup.Builder(
-                                SafetyCenterCtsData.entryGroupId(DYNAMIC_GROUP_ID), "OK")
+                        SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
                             .setSeverityLevel(ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING)
                             .setSummary("Critical summary")
                             .setEntries(
@@ -529,10 +542,10 @@
                     SafetyCenterStaticEntryGroup(
                         "OK",
                         listOf(
-                            rigidEntryUpdated,
-                            rigidEntryForWorkUpdated,
-                            rigidEntry,
-                            rigidEntryForWork))))
+                            staticEntryUpdated,
+                            staticEntryForWorkUpdated,
+                            staticEntry,
+                            staticEntryForWork))))
         assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataFromComplexConfig)
     }
 
@@ -555,8 +568,7 @@
                 primaryProfileOnlyIssues,
                 listOf(
                     SafetyCenterEntryOrGroup(
-                        SafetyCenterEntryGroup.Builder(
-                                SafetyCenterCtsData.entryGroupId(DYNAMIC_GROUP_ID), "OK")
+                        SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
                             .setSeverityLevel(ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING)
                             .setSummary("Critical summary")
                             .setEntries(
@@ -578,10 +590,10 @@
                     SafetyCenterStaticEntryGroup(
                         "OK",
                         listOf(
-                            rigidEntryUpdated,
-                            rigidEntryForWorkPaused,
-                            rigidEntry,
-                            rigidEntryForWorkPaused))))
+                            staticEntryUpdated,
+                            staticEntryForWorkPaused,
+                            staticEntry,
+                            staticEntryForWorkPaused))))
         assertThat(apiSafetyCenterData).isEqualTo(safetyCenterDataFromComplexConfig)
     }
 
@@ -620,8 +632,7 @@
                 emptyList(),
                 listOf(
                     SafetyCenterEntryOrGroup(
-                        SafetyCenterEntryGroup.Builder(
-                                SafetyCenterCtsData.entryGroupId(SINGLE_SOURCE_GROUP_ID), "OK")
+                        SafetyCenterEntryGroup.Builder(SINGLE_SOURCE_GROUP_ID, "OK")
                             .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN)
                             .setSummary(
                                 safetyCenterResourcesContext.getStringByName(
@@ -915,9 +926,10 @@
         safetyCenterCtsHelper.setData(
             ISSUE_ONLY_ALL_OPTIONAL_ID,
             SafetySourceCtsData.issuesOnly(safetySourceCtsData.recommendationGeneralIssue))
-        safetyCenterCtsHelper.setData(DYNAMIC_IN_RIGID_ID, safetySourceCtsData.unspecifiedWithIssue)
         safetyCenterCtsHelper.setData(
-            ISSUE_ONLY_IN_RIGID_ID,
+            DYNAMIC_IN_STATELESS_ID, safetySourceCtsData.unspecifiedWithIssue)
+        safetyCenterCtsHelper.setData(
+            ISSUE_ONLY_IN_STATELESS_ID,
             SafetySourceCtsData.issuesOnly(safetySourceCtsData.informationIssue))
     }
 
@@ -932,9 +944,9 @@
             ISSUE_ONLY_ALL_OPTIONAL_ID,
             SafetySourceCtsData.issuesOnly(safetySourceCtsData.informationIssue))
         managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
-            DYNAMIC_IN_RIGID_ID, safetySourceCtsData.unspecifiedWithIssueForWork)
+            DYNAMIC_IN_STATELESS_ID, safetySourceCtsData.unspecifiedWithIssueForWork)
         managedSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
-            ISSUE_ONLY_IN_RIGID_ID,
+            ISSUE_ONLY_IN_STATELESS_ID,
             SafetySourceCtsData.issuesOnly(safetySourceCtsData.informationIssue))
     }
 }
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterNotificationTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterNotificationTest.kt
new file mode 100644
index 0000000..8c3b28c
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterNotificationTest.kt
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.safetycenter.cts
+
+import android.content.Context
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+import android.safetycenter.SafetyCenterManager
+import android.safetycenter.SafetySourceIssue
+import android.safetycenter.cts.testing.Coroutines
+import android.safetycenter.cts.testing.CtsNotificationListener
+import android.safetycenter.cts.testing.NotificationCharacteristics
+import android.safetycenter.cts.testing.NotificationCharacteristics.Companion.assertNotificationMatches
+import android.safetycenter.cts.testing.NotificationCharacteristics.Companion.assertNotificationsMatch
+import android.safetycenter.cts.testing.SafetyCenterApisWithShellPermissions.clearAllSafetySourceDataForTestsWithPermission
+import android.safetycenter.cts.testing.SafetyCenterApisWithShellPermissions.dismissSafetyCenterIssueWithPermission
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_CONFIG
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_ID
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.dynamicSafetySourceBuilder
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.singleSourceConfig
+import android.safetycenter.cts.testing.SafetyCenterCtsData
+import android.safetycenter.cts.testing.SafetyCenterCtsHelper
+import android.safetycenter.cts.testing.SafetyCenterFlags
+import android.safetycenter.cts.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
+import android.safetycenter.cts.testing.SafetySourceCtsData
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.android.compatibility.common.util.SystemUtil
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertFailsWith
+import kotlinx.coroutines.TimeoutCancellationException
+import org.junit.After
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Notification-related tests for [SafetyCenterManager]. */
+@RunWith(AndroidJUnit4::class)
+class SafetyCenterNotificationTest {
+    private val context: Context = getApplicationContext()
+    private val safetyCenterCtsHelper = SafetyCenterCtsHelper(context)
+    private val safetySourceCtsData = SafetySourceCtsData(context)
+    private val safetyCenterManager =
+        requireNotNull(context.getSystemService(SafetyCenterManager::class.java)) {
+            "Could not get system service"
+        }
+
+    // JUnit's Assume is not supported in @BeforeClass by the CTS tests runner, so this is used to
+    // manually skip the setup and teardown methods.
+    private val shouldRunTests = context.deviceSupportsSafetyCenter()
+
+    @Before
+    fun assumeDeviceSupportsSafetyCenterToRunTests() {
+        assumeTrue(shouldRunTests)
+    }
+
+    @Before
+    fun setUp() {
+        if (!shouldRunTests) {
+            return
+        }
+        safetyCenterCtsHelper.setup()
+        CtsNotificationListener.toggleListenerAccess(true)
+        SafetyCenterFlags.notificationsEnabled = true
+        SafetyCenterFlags.notificationsAllowedSources = setOf(SINGLE_SOURCE_ID)
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+    }
+
+    @After
+    fun tearDown() {
+        if (!shouldRunTests) {
+            return
+        }
+        CtsNotificationListener.toggleListenerAccess(false)
+        safetyCenterCtsHelper.reset()
+    }
+
+    @Test
+    fun setSafetySourceData_withNoIssue_noNotification() {
+        CtsNotificationListener.assertNoNotificationsPosted {
+            safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, safetySourceCtsData.information)
+        }
+    }
+
+    @Test
+    fun setSafetySourceData_withoutNotificationsAllowedSource_noNotification() {
+        SafetyCenterFlags.notificationsAllowedSources = emptySet()
+
+        CtsNotificationListener.assertNoNotificationsPosted {
+            safetyCenterCtsHelper.setData(
+                SINGLE_SOURCE_ID, safetySourceCtsData.recommendationWithAccountIssue)
+        }
+    }
+
+    @Test
+    fun setSafetySourceData_withFlagDisabled_noNotification() {
+        SafetyCenterFlags.notificationsEnabled = false
+
+        CtsNotificationListener.assertNoNotificationsPosted {
+            safetyCenterCtsHelper.setData(
+                SINGLE_SOURCE_ID, safetySourceCtsData.recommendationWithAccountIssue)
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun setSafetySourceData_withNotificationBehaviorNever_noNotification() {
+        val data =
+            safetySourceCtsData
+                .defaultRecommendationDataBuilder()
+                .addIssue(
+                    safetySourceCtsData
+                        .defaultRecommendationIssueBuilder()
+                        .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_NEVER)
+                        .build())
+                .build()
+        CtsNotificationListener.assertNoNotificationsPosted {
+            safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data)
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun setSafetySourceData_withNotificationBehaviorImmediately_sendsNotification() {
+        val data =
+            safetySourceCtsData
+                .defaultRecommendationDataBuilder()
+                .addIssue(
+                    safetySourceCtsData
+                        .defaultRecommendationIssueBuilder("Notify immediately", "This is urgent!")
+                        .setNotificationBehavior(
+                            SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY)
+                        .build())
+                .build()
+
+        val notification =
+            CtsNotificationListener.getNextNotificationPostedOrNull {
+                safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data)
+            }
+
+        assertThat(notification).isNotNull()
+        assertNotificationMatches(
+            notification!!,
+            NotificationCharacteristics(title = "Notify immediately", text = "This is urgent!"))
+    }
+
+    @Test
+    fun setSafetySourceData_withNotificationsAllowedForSourceByFlag_sendsNotification() {
+        SafetyCenterFlags.notificationsAllowedSources = setOf(SINGLE_SOURCE_ID)
+        val data = safetySourceCtsData.recommendationWithAccountIssue
+
+        val notification =
+            CtsNotificationListener.getNextNotificationPostedOrNull {
+                safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data)
+            }
+
+        assertThat(notification).isNotNull()
+        assertNotificationMatches(
+            notification!!,
+            NotificationCharacteristics(
+                title = "Recommendation issue title", text = "Recommendation issue summary"))
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun setSafetySourceData_withNotificationsAllowedForSourceByConfig_sendsNotification() {
+        safetyCenterCtsHelper.setConfig(
+            singleSourceConfig(
+                dynamicSafetySourceBuilder("MyNotifiableSource")
+                    .setNotificationsAllowed(true)
+                    .build()))
+        val data = safetySourceCtsData.recommendationWithAccountIssue
+
+        val notification =
+            CtsNotificationListener.getNextNotificationPostedOrNull {
+                safetyCenterCtsHelper.setData("MyNotifiableSource", data)
+            }
+
+        assertThat(notification).isNotNull()
+        assertNotificationMatches(
+            notification!!,
+            NotificationCharacteristics(
+                title = "Recommendation issue title", text = "Recommendation issue summary"))
+    }
+
+    @Test
+    fun setSafetySourceData_twiceWithSameIssueId_updatesNotification() {
+        val data1 =
+            safetySourceCtsData
+                .defaultRecommendationDataBuilder()
+                .addIssue(
+                    safetySourceCtsData
+                        .defaultRecommendationIssueBuilder("Initial", "Blah")
+                        .build())
+                .build()
+        val data2 =
+            safetySourceCtsData
+                .defaultRecommendationDataBuilder()
+                .addIssue(
+                    safetySourceCtsData
+                        .defaultRecommendationIssueBuilder("Revised", "Different")
+                        .build())
+                .build()
+
+        val notifications =
+            CtsNotificationListener.getAllNotificationsPosted {
+                safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data1)
+                safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data2)
+            }
+
+        assertNotificationsMatch(
+            notifications,
+            NotificationCharacteristics(title = "Initial", text = "Blah"),
+            NotificationCharacteristics(title = "Revised", text = "Different"))
+        assertThat(notifications[0].key).isEqualTo(notifications[1].key)
+    }
+
+    @Test
+    fun setSafetySourceData_twiceWithExactSameIssue_doNotNotifyTwice() {
+        val data = safetySourceCtsData.recommendationWithAccountIssue
+
+        val notifications =
+            CtsNotificationListener.getAllNotificationsPosted {
+                safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data)
+                safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data)
+            }
+
+        assertNotificationsMatch(
+            notifications,
+            NotificationCharacteristics(
+                title = "Recommendation issue title", text = "Recommendation issue summary"))
+    }
+
+    @Test
+    fun setSafetySourceData_twiceRemovingAnIssue_cancelsNotification() {
+        val data1 = safetySourceCtsData.recommendationWithAccountIssue
+        val data2 = safetySourceCtsData.information
+
+        val posted =
+            CtsNotificationListener.getNextNotificationPostedOrNull {
+                safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data1)
+            }
+        val removed =
+            CtsNotificationListener.getNextNotificationRemovedOrNull {
+                safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data2)
+            }
+
+        assertThat(posted).isNotNull()
+        assertThat(removed).isNotNull()
+        assertThat(removed!!.key).isEqualTo(posted!!.key)
+    }
+
+    @Test
+    fun setSafetySourceData_withDismissedIssueId_doesNotNotify() {
+        val data1 =
+            safetySourceCtsData
+                .defaultRecommendationDataBuilder()
+                .addIssue(
+                    safetySourceCtsData
+                        .defaultRecommendationIssueBuilder("Initial", "Blah")
+                        .build())
+                .build()
+        val data2 =
+            safetySourceCtsData
+                .defaultRecommendationDataBuilder()
+                .addIssue(
+                    safetySourceCtsData
+                        .defaultRecommendationIssueBuilder("Revised", "Different")
+                        .build())
+                .build()
+
+        val posted =
+            CtsNotificationListener.getNextNotificationPostedOrNull {
+                safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data1)
+            }
+
+        assertThat(posted).isNotNull()
+        assertNotificationMatches(
+            posted!!, NotificationCharacteristics(title = "Initial", text = "Blah"))
+
+        CtsNotificationListener.cancelAndWait(posted.key)
+
+        // Here we wait for the issue (there is only one) to be recorded as dismissed according to
+        // the dumpsys output. The cancelAndWait helper above "waits" for the notification to be
+        // dismissed, but it does not wait for the notification's delete PendingIntent to be
+        // handled. Without this additional wait there is a race condition between
+        // SafetyCenterNotificationReceiver#onReceive and the setData below. That race makes the
+        // test is flaky because the notification may not be recorded as dismissed before setData
+        // is called again and the notification is able to be posted again, contradicting the
+        // assertion.
+        Coroutines.waitForWithTimeout {
+            val dump = SystemUtil.runShellCommand("dumpsys safety_center")
+            dump.contains(Regex("""mNotificationDismissedAt=\d+"""))
+        }
+
+        CtsNotificationListener.assertNoNotificationsPosted {
+            safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data2)
+        }
+    }
+
+    @Test
+    fun dismissSafetyCenterIssue_dismissesNotification() {
+        val posted =
+            CtsNotificationListener.getNextNotificationPostedOrNull {
+                safetyCenterCtsHelper.setData(
+                    SINGLE_SOURCE_ID, safetySourceCtsData.recommendationWithAccountIssue)
+            }
+        val removed =
+            CtsNotificationListener.getNextNotificationRemovedOrNull {
+                safetyCenterManager.dismissSafetyCenterIssueWithPermission(
+                    SafetyCenterCtsData.issueId(
+                        SINGLE_SOURCE_ID, SafetySourceCtsData.RECOMMENDATION_ISSUE_ID))
+            }
+
+        assertThat(posted).isNotNull()
+        assertThat(removed).isNotNull()
+        assertThat(removed!!.key).isEqualTo(posted!!.key)
+    }
+
+    @Test
+    fun dismissingNotification_doesntUpdateSafetyCenterData() {
+        val posted =
+            CtsNotificationListener.getNextNotificationPostedOrNull {
+                safetyCenterCtsHelper.setData(
+                    SINGLE_SOURCE_ID, safetySourceCtsData.criticalWithResolvingGeneralIssue)
+            }
+        assertThat(posted).isNotNull()
+        val listener = safetyCenterCtsHelper.addListener()
+
+        CtsNotificationListener.cancelAndWait(posted!!.key)
+
+        assertFailsWith(TimeoutCancellationException::class) {
+            listener.receiveSafetyCenterData(Coroutines.TIMEOUT_SHORT)
+        }
+    }
+
+    @Test
+    fun clearSafetySourceData_cancelsAllNotifications() {
+        val data = safetySourceCtsData.recommendationWithAccountIssue
+
+        CtsNotificationListener.assertAnyNotificationPosted {
+            safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data)
+        }
+        CtsNotificationListener.assertAnyNotificationRemoved {
+            safetyCenterManager.clearAllSafetySourceDataForTestsWithPermission()
+        }
+        val postedAfter =
+            CtsNotificationListener.getNextNotificationPostedOrNull {
+                safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data)
+            }
+
+        assertThat(postedAfter).isNotNull()
+        assertNotificationMatches(
+            postedAfter!!,
+            NotificationCharacteristics(
+                title = "Recommendation issue title", text = "Recommendation issue summary"))
+    }
+}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterShellCommandsTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterShellCommandsTest.kt
new file mode 100644
index 0000000..a9f489d
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterShellCommandsTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.safetycenter.cts
+
+import android.content.Context
+import android.safetycenter.SafetyCenterManager
+import android.safetycenter.cts.testing.SafetyCenterApisWithShellPermissions.isSafetyCenterEnabledWithPermission
+import android.safetycenter.cts.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compatibility.common.util.SystemUtil
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** CTS tests for Safety Center's shell commands. */
+@RunWith(AndroidJUnit4::class)
+class SafetyCenterShellCommandsTest {
+    private val context: Context = getApplicationContext()
+
+    @Test
+    fun enabled_printsEnabledValue() {
+        val enabled = executeShellCommand("cmd safety_center enabled").toBoolean()
+
+        val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!!
+        assertThat(enabled).isEqualTo(safetyCenterManager.isSafetyCenterEnabledWithPermission())
+    }
+
+    @Test
+    fun supported_printsSupportedValue() {
+        val supported = executeShellCommand("cmd safety_center supported").toBoolean()
+
+        assertThat(supported).isEqualTo(context.deviceSupportsSafetyCenter())
+    }
+
+    @Test
+    fun packageName_printsPackageName() {
+        val packageName = executeShellCommand("cmd safety_center package-name")
+
+        assertThat(packageName).isEqualTo(context.packageManager.permissionControllerPackageName)
+    }
+
+    private fun executeShellCommand(command: String): String =
+        SystemUtil.runShellCommand(command).trim()
+}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt
index 9b40fd6..1947386 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryGroupTest.kt
@@ -98,7 +98,8 @@
 
     @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetyCenterStaticEntryGroup.CREATOR)
             .addEqualityGroup(
                 staticEntryGroup,
                 SafetyCenterStaticEntryGroup("a title", listOf(staticEntry1, staticEntry2)))
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt
index 279187e..65dd2c6 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStaticEntryTest.kt
@@ -95,14 +95,15 @@
 
     @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetyCenterStaticEntry.CREATOR,
+                createCopy = { SafetyCenterStaticEntry.Builder(it).build() })
             .addEqualityGroup(
                 staticEntry1,
                 SafetyCenterStaticEntry.Builder("a title")
                     .setSummary("a summary")
                     .setPendingIntent(pendingIntent1)
-                    .build(),
-                SafetyCenterStaticEntry.Builder(staticEntry1).build())
+                    .build())
             .addEqualityGroup(staticEntry2)
             .addEqualityGroup(staticEntryMinimal, SafetyCenterStaticEntry.Builder("").build())
             .addEqualityGroup(
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt
index e9449ec..646409a 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterStatusTest.kt
@@ -147,7 +147,9 @@
 
     @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetyCenterStatus.CREATOR,
+                createCopy = { SafetyCenterStatus.Builder(it).build() })
             .addEqualityGroup(
                 baseStatus,
                 SafetyCenterStatus.Builder("This is my title", "This is my summary")
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt
index 73de7c5..e39734c 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterUnsupportedTest.kt
@@ -54,6 +54,8 @@
 import android.support.test.uiautomator.By
 import androidx.test.core.app.ApplicationProvider.getApplicationContext
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compatibility.common.util.DisableAnimationRule
+import com.android.compatibility.common.util.FreezeRotationRule
 import com.google.common.truth.Truth.assertThat
 import com.google.common.util.concurrent.MoreExecutors.directExecutor
 import kotlin.test.assertFailsWith
@@ -61,12 +63,18 @@
 import org.junit.After
 import org.junit.Assume.assumeTrue
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
 /** CTS tests for our APIs and UI on devices that do not support Safety Center. */
 @RunWith(AndroidJUnit4::class)
 class SafetyCenterUnsupportedTest {
+
+    @get:Rule val disableAnimationRule = DisableAnimationRule()
+
+    @get:Rule val freezeRotationRule = FreezeRotationRule()
+
     private val context: Context = getApplicationContext()
     private val safetyCenterCtsHelper = SafetyCenterCtsHelper(context)
     private val safetySourceCtsData = SafetySourceCtsData(context)
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt
index dff7918..57afc80 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyEventTest.kt
@@ -16,6 +16,7 @@
 
 package android.safetycenter.cts
 
+import android.os.Build
 import android.safetycenter.SafetyEvent
 import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED
 import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_REBOOTED
@@ -25,6 +26,7 @@
 import android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.ext.truth.os.ParcelableSubject.assertThat
+import androidx.test.filters.SdkSuppress
 import com.android.permission.testing.EqualsHashCodeToStringTester
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.assertFailsWith
@@ -180,7 +182,22 @@
 
     @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        newTiramisuEqualsHashCodeToStringTester().test()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun equalsHashCodeToString_usingEqualsHashCodeToStringTester_atLeastAndroidU() {
+        newTiramisuEqualsHashCodeToStringTester(
+                createCopyFromBuilder = { SafetyEvent.Builder(it).build() })
+            .test()
+    }
+
+    private fun newTiramisuEqualsHashCodeToStringTester(
+        createCopyFromBuilder: ((SafetyEvent) -> SafetyEvent)? = null
+    ) =
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetyEvent.CREATOR, createCopy = createCopyFromBuilder)
             .addEqualityGroup(SafetyEvent.Builder(SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build())
             .addEqualityGroup(
                 SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
@@ -233,8 +250,6 @@
                     .setSafetySourceIssueId(SAFETY_SOURCE_ISSUE_ID)
                     .setSafetySourceIssueActionId(OTHER_SAFETY_SOURCE_ISSUE_ACTION_ID)
                     .build())
-            .test()
-    }
 
     companion object {
         private const val REFRESH_BROADCAST_ID = "refresh_broadcast_id"
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt
index b50ea7f..7554d9a 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceDataTest.kt
@@ -20,6 +20,8 @@
 import android.app.PendingIntent.FLAG_IMMUTABLE
 import android.content.Context
 import android.content.Intent
+import android.os.Build
+import android.os.Bundle
 import android.safetycenter.SafetySourceData
 import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING
 import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_INFORMATION
@@ -27,9 +29,12 @@
 import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED
 import android.safetycenter.SafetySourceIssue
 import android.safetycenter.SafetySourceStatus
+import androidx.core.os.bundleOf
 import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.core.os.Parcelables.forceParcel
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.ext.truth.os.ParcelableSubject.assertThat
+import androidx.test.filters.SdkSuppress
 import com.android.permission.testing.EqualsHashCodeToStringTester
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.assertFailsWith
@@ -91,6 +96,28 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getExtras_withDefaultBuilder_returnsEmptyBundle() {
+        val safetySourceData =
+            SafetySourceData.Builder().setStatus(createStatus(SEVERITY_LEVEL_INFORMATION)).build()
+
+        assertThat(safetySourceData.extras.keySet()).isEmpty()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getExtras_whenSetExplicitly_returnsExtras() {
+        val safetySourceData =
+            SafetySourceData.Builder()
+                .setStatus(createStatus(SEVERITY_LEVEL_INFORMATION))
+                .setExtras(bundleOf(EXTRA_KEY to EXTRA_VALUE))
+                .build()
+
+        assertThat(safetySourceData.extras.keySet()).containsExactly(EXTRA_KEY)
+        assertThat(safetySourceData.extras.getString(EXTRA_KEY, "")).isEqualTo(EXTRA_VALUE)
+    }
+
+    @Test
     fun builder_addIssue_doesNotMutatePreviouslyBuiltInstance() {
         val firstIssue = createIssue(SEVERITY_LEVEL_INFORMATION, 1)
         val secondIssue = createIssue(SEVERITY_LEVEL_INFORMATION, 2)
@@ -285,12 +312,29 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun parcelRoundTrip_withExtras_recreatesEqual() {
+        val safetySourceData =
+            SafetySourceData.Builder()
+                .setStatus(createStatus(SEVERITY_LEVEL_RECOMMENDATION))
+                .addIssue(createIssue(SEVERITY_LEVEL_RECOMMENDATION, 1))
+                .addIssue(createIssue(SEVERITY_LEVEL_INFORMATION, 2))
+                .setExtras(bundleOf(EXTRA_KEY to EXTRA_VALUE))
+                .build()
+        val recreatedSafetySourceData = forceParcel(safetySourceData, SafetySourceData.CREATOR)
+
+        assertThat(recreatedSafetySourceData).isEqualTo(safetySourceData)
+        assertThat(recreatedSafetySourceData.extras.keySet()).containsExactly(EXTRA_KEY)
+        assertThat(recreatedSafetySourceData.extras.getString(EXTRA_KEY, "")).isEqualTo(EXTRA_VALUE)
+    }
+
+    @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
         val firstStatus = createStatus(SEVERITY_LEVEL_INFORMATION, 1)
         val secondStatus = createStatus(SEVERITY_LEVEL_INFORMATION, 2)
         val firstIssue = createIssue(SEVERITY_LEVEL_INFORMATION, 1)
         val secondIssue = createIssue(SEVERITY_LEVEL_INFORMATION, 2)
-        EqualsHashCodeToStringTester()
+        EqualsHashCodeToStringTester.ofParcelable(parcelableCreator = SafetySourceData.CREATOR)
             .addEqualityGroup(
                 SafetySourceData.Builder().setStatus(firstStatus).build(),
                 SafetySourceData.Builder().setStatus(firstStatus).build())
@@ -321,6 +365,93 @@
             .test()
     }
 
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun equalsHashCode_atLeastU_usingEqualsHashCodeToStringTester() {
+        val firstStatus = createStatus(SEVERITY_LEVEL_INFORMATION, 1)
+        val secondStatus = createStatus(SEVERITY_LEVEL_INFORMATION, 2)
+        val firstIssue = createIssue(SEVERITY_LEVEL_INFORMATION, 1)
+        val secondIssue = createIssue(SEVERITY_LEVEL_INFORMATION, 2)
+        val filledExtras = bundleOf(EXTRA_KEY to EXTRA_VALUE)
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetySourceData.CREATOR,
+                ignoreToString = true,
+                createCopy = { SafetySourceData.Builder(it).build() })
+            .addEqualityGroup(
+                SafetySourceData.Builder().setStatus(firstStatus).build(),
+                SafetySourceData.Builder().setStatus(firstStatus).setExtras(filledExtras).build())
+            .addEqualityGroup(
+                SafetySourceData.Builder().addIssue(firstIssue).addIssue(secondIssue).build(),
+                SafetySourceData.Builder()
+                    .addIssue(firstIssue)
+                    .addIssue(secondIssue)
+                    .setExtras(filledExtras)
+                    .build())
+            .addEqualityGroup(
+                SafetySourceData.Builder()
+                    .setStatus(firstStatus)
+                    .addIssue(firstIssue)
+                    .addIssue(secondIssue)
+                    .build(),
+                SafetySourceData.Builder()
+                    .setStatus(firstStatus)
+                    .addIssue(firstIssue)
+                    .addIssue(secondIssue)
+                    .setExtras(filledExtras)
+                    .build())
+            .addEqualityGroup(
+                SafetySourceData.Builder().setStatus(secondStatus).build(),
+                SafetySourceData.Builder().setStatus(secondStatus).setExtras(filledExtras).build())
+            .addEqualityGroup(
+                SafetySourceData.Builder().addIssue(secondIssue).addIssue(firstIssue).build(),
+                SafetySourceData.Builder()
+                    .addIssue(secondIssue)
+                    .addIssue(firstIssue)
+                    .setExtras(filledExtras)
+                    .build())
+            .addEqualityGroup(
+                SafetySourceData.Builder().addIssue(firstIssue).build(),
+                SafetySourceData.Builder().addIssue(firstIssue).setExtras(filledExtras).build())
+            .addEqualityGroup(
+                SafetySourceData.Builder()
+                    .setStatus(secondStatus)
+                    .addIssue(firstIssue)
+                    .addIssue(secondIssue)
+                    .build(),
+                SafetySourceData.Builder()
+                    .setStatus(secondStatus)
+                    .addIssue(firstIssue)
+                    .addIssue(secondIssue)
+                    .setExtras(filledExtras)
+                    .build())
+            .test()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun toString_withExtras_containsHasExtras() {
+        val safetySourceDataWithExtras =
+            SafetySourceData.Builder()
+                .setStatus(createStatus(SEVERITY_LEVEL_INFORMATION))
+                .setExtras(bundleOf(EXTRA_KEY to EXTRA_VALUE))
+                .build()
+
+        val stringRepresentation = safetySourceDataWithExtras.toString()
+
+        assertThat(stringRepresentation).contains("(has extras)")
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun toString_withoutExtras_doesNotContainHasExtras() {
+        val safetySourceDataWithoutExtras =
+            SafetySourceData.Builder().setStatus(createStatus(SEVERITY_LEVEL_INFORMATION)).build()
+
+        val stringRepresentation = safetySourceDataWithoutExtras.toString()
+
+        assertThat(stringRepresentation).doesNotContain("(has extras)")
+    }
+
     private fun createStatus(severityLevel: Int, id: Int = 0) =
         SafetySourceStatus.Builder("Status title $id", "Status summary $id", severityLevel)
             .setPendingIntent(
@@ -349,4 +480,12 @@
                             FLAG_IMMUTABLE))
                     .build())
             .build()
+
+    private companion object {
+        /** Key of extra data in [Bundle]. */
+        const val EXTRA_KEY = "extra_key"
+
+        /** Value of extra data in [Bundle]. */
+        const val EXTRA_VALUE = "extra_value"
+    }
 }
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt
index 43902d7..735e543 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceErrorDetailsTest.kt
@@ -44,7 +44,8 @@
 
     @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetySourceErrorDetails.CREATOR)
             .addEqualityGroup(
                 SafetySourceErrorDetails(SAFETY_EVENT),
                 SafetySourceErrorDetails(
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt
index 5957956..6f9a09f 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceIssueTest.kt
@@ -20,6 +20,9 @@
 import android.app.PendingIntent.FLAG_IMMUTABLE
 import android.content.Context
 import android.content.Intent
+import android.os.Build
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
 import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING
 import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_INFORMATION
 import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED
@@ -28,13 +31,18 @@
 import android.safetycenter.SafetySourceIssue.ISSUE_CATEGORY_ACCOUNT
 import android.safetycenter.SafetySourceIssue.ISSUE_CATEGORY_DEVICE
 import android.safetycenter.SafetySourceIssue.ISSUE_CATEGORY_GENERAL
+import android.safetycenter.SafetySourceIssue.Notification
 import android.safetycenter.cts.testing.Generic
+import androidx.annotation.RequiresApi
 import androidx.test.core.app.ApplicationProvider.getApplicationContext
 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.testing.EqualsHashCodeToStringTester
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.assertFailsWith
+import org.junit.Assume.assumeFalse
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -146,36 +154,200 @@
 
     @Test
     fun action_equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        actionNewTiramisuEqualsHashCodeToStringTester().test()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun action_equalsHashCodeToString_usingEqualsHashCodeToStringTester_atLeastAndroidU() {
+        actionNewTiramisuEqualsHashCodeToStringTester(
+                createCopyFromBuilder = { Action.Builder(it).build() })
+            .test()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_getTitle_returnsTitle() {
+        val notification = Notification.Builder("Notification title", "Notification text").build()
+
+        assertThat(notification.title).isEqualTo("Notification title")
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_getText_returnsText() {
+        val notification = Notification.Builder("Notification title", "Notification text").build()
+
+        assertThat(notification.text).isEqualTo("Notification text")
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_getActions_withDefaultBuilder_returnsEmptyList() {
+        val notification = Notification.Builder("", "").build()
+
+        assertThat(notification.actions).isEmpty()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_getActions_returnsActions() {
+        val notification =
+            Notification.Builder("", "").addAction(action1).addAction(action2).build()
+
+        assertThat(notification.actions).containsExactly(action1, action2).inOrder()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_getActions_mutationsAreNotAllowed() {
+        val notification =
+            Notification.Builder("", "").addAction(action1).addAction(action2).build()
+
+        assertFailsWith(UnsupportedOperationException::class) { notification.actions.add(action3) }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_describeContents_returns0() {
+        val notification =
+            Notification.Builder("Notification title", "Notification text")
+                .addAction(action1)
+                .addAction(action2)
+                .build()
+
+        assertThat(notification.describeContents()).isEqualTo(0)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_parcelRoundTrip_recreatesEqual() {
+        val notification =
+            Notification.Builder("Notification title", "Notification text")
+                .addAction(action1)
+                .addAction(action2)
+                .build()
+
+        assertThat(notification).recreatesEqual(Notification.CREATOR)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_builder_withNullTitle_throwsNullPointerException() {
+        assertFailsWith(NullPointerException::class) {
+            Notification.Builder(Generic.asNull(), "Notification text")
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_builder_withNullText_throwsNullPointerException() {
+        assertFailsWith(NullPointerException::class) {
+            Notification.Builder("Notification title", Generic.asNull())
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_builder_addAction_doesNotMutatePreviouslyBuiltInstance() {
+        val notificationBuilder = Notification.Builder("", "").addAction(action1)
+        val actions = notificationBuilder.build().actions
+
+        notificationBuilder.addAction(action2)
+
+        assertThat(actions).containsExactly(action1)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_builder_addAction_withNull_throwsIllegalArgumentException() {
+        assertFailsWith(NullPointerException::class) {
+            Notification.Builder("", "").addAction(Generic.asNull())
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_builder_setActions_withNull_throwsIllegalArgumentException() {
+        assertFailsWith(NullPointerException::class) {
+            Notification.Builder("", "").setActions(Generic.asNull())
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_builder_setActions_removesAllPreviouslyAddedActions() {
+        val notification =
+            Notification.Builder("", "")
+                .addAction(action1)
+                .addAction(action2)
+                .setActions(listOf(action3))
+                .build()
+
+        assertThat(notification.actions).containsExactly(action3)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_builder_clearActions_removesAllActions() {
+        val notification =
+            Notification.Builder("", "")
+                .addAction(action1)
+                .addAction(action2)
+                .clearActions()
+                .addAction(action3)
+                .build()
+
+        assertThat(notification.actions).containsExactly(action3)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_build_withDuplicateActionIds_throwsIllegalArgumentException() {
+        val notificationBuilder =
+            Notification.Builder("Notification title", "Notification text")
+                .addAction(action1)
+                .addAction(action1)
+
+        val exception =
+            assertFailsWith(IllegalArgumentException::class) { notificationBuilder.build() }
+        assertThat(exception)
+            .hasMessageThat()
+            .isEqualTo("Custom notification cannot have duplicate action ids")
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_build_withMoreThanTwoActions_throwsIllegalArgumentException() {
+        val notificationBuilder =
+            Notification.Builder("Notification title", "Notification text")
+                .addAction(action1)
+                .addAction(action2)
+                .addAction(action3)
+
+        val exception =
+            assertFailsWith(IllegalArgumentException::class) { notificationBuilder.build() }
+        assertThat(exception)
+            .hasMessageThat()
+            .isEqualTo("Custom notification must not contain more than 2 actions")
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun notification_equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = Notification.CREATOR,
+                createCopy = { Notification.Builder(it).build() })
             .addEqualityGroup(
-                Action.Builder("action_id", "Action label", pendingIntent1).build(),
-                Action.Builder("action_id", "Action label", pendingIntent1).build(),
-                Action.Builder("action_id", "Action label", pendingIntent1)
-                    .setWillResolve(false)
-                    .build())
+                Notification.Builder("Title", "Text").build(),
+                Notification.Builder("Title", "Text").build(),
+            )
+            .addEqualityGroup(Notification.Builder("Other title", "Text").build())
+            .addEqualityGroup(Notification.Builder("Title", "Other text").build())
+            .addEqualityGroup(Notification.Builder("Title", "Text").addAction(action1).build())
+            .addEqualityGroup(Notification.Builder("Title", "Text").addAction(action2).build())
             .addEqualityGroup(
-                Action.Builder("action_id", "Action label", pendingIntent1)
-                    .setSuccessMessage("Action successfully completed")
-                    .build())
-            .addEqualityGroup(
-                Action.Builder("action_id", "Other action label", pendingIntent1).build())
-            .addEqualityGroup(
-                Action.Builder("other_action_id", "Action label", pendingIntent1).build())
-            .addEqualityGroup(
-                Action.Builder("action_id", "Action label", pendingIntent1)
-                    .setWillResolve(true)
-                    .build())
-            .addEqualityGroup(
-                Action.Builder(
-                        "action_id",
-                        "Action label",
-                        PendingIntent.getActivity(
-                            context, 0, Intent("Other action PendingIntent"), FLAG_IMMUTABLE))
-                    .build())
-            .addEqualityGroup(
-                Action.Builder("action_id", "Action label", pendingIntent1)
-                    .setSuccessMessage("Other action successfully completed")
-                    .build())
+                Notification.Builder("Title", "Text").addAction(action1).addAction(action2).build())
             .test()
     }
 
@@ -256,6 +428,75 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getAttributionTitle_withNullAttributionTitle_returnsNull() {
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+                .build()
+
+        assertThat(safetySourceIssue.attributionTitle).isNull()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getAttributionTitle_returnsAttributionTitle() {
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+                .setAttributionTitle("attribution title")
+                .build()
+
+        assertThat(safetySourceIssue.attributionTitle).isEqualTo("attribution title")
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = TIRAMISU)
+    fun getAttributionTitle_withVersionLessThanU_throwsUnsupportedOperationException() {
+        // TODO(b/258228790): Remove after U is no longer in pre-release
+        assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+                .build()
+
+        assertFailsWith(UnsupportedOperationException::class) { safetySourceIssue.attributionTitle }
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = TIRAMISU)
+    fun setAttributionTitle_withVersionLessThanU_throwsUnsupportedOperationException() {
+        // TODO(b/258228790): Remove after U is no longer in pre-release
+        assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
+        val safetySourceIssueBuilder =
+            SafetySourceIssue.Builder(
+                "Issue id",
+                "Issue title",
+                "Issue summary",
+                SEVERITY_LEVEL_INFORMATION,
+                "issue_type_id")
+
+        assertFailsWith(UnsupportedOperationException::class) {
+            safetySourceIssueBuilder.setAttributionTitle("title")
+        }
+    }
+
+    @Test
     fun getSeverityLevel_returnsSeverityLevel() {
         val safetySourceIssue =
             SafetySourceIssue.Builder(
@@ -302,6 +543,24 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getIssueCategory_whenSetExplicitlyWithUValueOnU_returnsIssueCategory() {
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+                .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS)
+                .build()
+
+        assertThat(safetySourceIssue.issueCategory)
+            .isEqualTo(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS)
+    }
+
+    @Test
     fun getActions_returnsActions() {
         val safetySourceIssue =
             SafetySourceIssue.Builder(
@@ -401,6 +660,75 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getDeduplicationId_withDefaultBuilder_returnsNull() {
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+                .build()
+
+        assertThat(safetySourceIssue.deduplicationId).isNull()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getDeduplicationId_whenSetExplicitly_returnsDeduplicationId() {
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+                .setDeduplicationId("deduplication_id")
+                .build()
+
+        assertThat(safetySourceIssue.deduplicationId).isEqualTo("deduplication_id")
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = TIRAMISU)
+    fun getDeduplicationId_withVersionLessThanU_throwsUnsupportedOperationException() {
+        // TODO(b/258228790): Remove after U is no longer in pre-release
+        assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+                .build()
+
+        assertFailsWith(UnsupportedOperationException::class) { safetySourceIssue.deduplicationId }
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = TIRAMISU)
+    fun setDeduplicationId_withVersionLessThanU_throwsUnsupportedOperationException() {
+        // TODO(b/258228790): Remove after U is no longer in pre-release
+        assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
+        val safetySourceIssueBuilder =
+            SafetySourceIssue.Builder(
+                "Issue id",
+                "Issue title",
+                "Issue summary",
+                SEVERITY_LEVEL_INFORMATION,
+                "issue_type_id")
+
+        assertFailsWith(UnsupportedOperationException::class) {
+            safetySourceIssueBuilder.setDeduplicationId("id")
+        }
+    }
+
+    @Test
     fun getIssueTypeId_returnsIssueTypeId() {
         val safetySourceIssue =
             SafetySourceIssue.Builder(
@@ -416,6 +744,268 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getCustomNotification_withDefaultBuilder_returnsNull() {
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+                .build()
+
+        assertThat(safetySourceIssue.customNotification).isNull()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getCustomNotification_whenSetExplicitly_returnsCustomNotification() {
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+                .setCustomNotification(
+                    Notification.Builder("Notification title", "Notification text")
+                        .addAction(action2)
+                        .build())
+                .build()
+
+        assertThat(safetySourceIssue.customNotification)
+            .isEqualTo(
+                Notification.Builder("Notification title", "Notification text")
+                    .addAction(action2)
+                    .build())
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = TIRAMISU)
+    fun getCustomNotification_withVersionLessThanU_throwsUnsupportedOperationException() {
+        // TODO(b/258228790): Remove after U is no longer in pre-release
+        assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+                .build()
+
+        assertFailsWith(UnsupportedOperationException::class) {
+            safetySourceIssue.customNotification
+        }
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = TIRAMISU)
+    fun setCustomNotification_withVersionLessThanU_throwsUnsupportedOperationException() {
+        // TODO(b/258228790): Remove after U is no longer in pre-release
+        assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
+        val safetySourceIssueBuilder =
+            SafetySourceIssue.Builder(
+                "Issue id",
+                "Issue title",
+                "Issue summary",
+                SEVERITY_LEVEL_INFORMATION,
+                "issue_type_id")
+
+        assertFailsWith(UnsupportedOperationException::class) {
+            safetySourceIssueBuilder.setCustomNotification(null)
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getNotificationBehavior_withDefaultBuilder_returnsUnspecified() {
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+                .build()
+
+        assertThat(safetySourceIssue.notificationBehavior)
+            .isEqualTo(SafetySourceIssue.NOTIFICATION_BEHAVIOR_UNSPECIFIED)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getNotificationBehavior_whenSetExplicitly_returnsSpecifiedBehavior() {
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+                .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_NEVER)
+                .build()
+
+        assertThat(safetySourceIssue.notificationBehavior)
+            .isEqualTo(SafetySourceIssue.NOTIFICATION_BEHAVIOR_NEVER)
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = TIRAMISU)
+    fun getNotificationBehavior_withVersionLessThanU_throwsUnsupportedOperationException() {
+        // TODO(b/258228790): Remove after U is no longer in pre-release
+        assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+                .build()
+
+        assertFailsWith(UnsupportedOperationException::class) {
+            safetySourceIssue.notificationBehavior
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun setNotificationBehavior_withInvalidNotificationBehavior_throwsIllegalArgumentException() {
+        val builder =
+            SafetySourceIssue.Builder(
+                "Issue id",
+                "Issue title",
+                "Issue summary",
+                SEVERITY_LEVEL_INFORMATION,
+                "issue_type_id")
+
+        val exception =
+            assertFailsWith(IllegalArgumentException::class) { builder.setNotificationBehavior(-1) }
+
+        assertThat(exception)
+            .hasMessageThat()
+            .isEqualTo("Unexpected NotificationBehavior for SafetySourceIssue: -1")
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = TIRAMISU)
+    fun setNotificationBehavior_withVersionLessThanU_throwsUnsupportedOperationException() {
+        // TODO(b/258228790): Remove after U is no longer in pre-release
+        assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
+        val safetySourceIssueBuilder =
+            SafetySourceIssue.Builder(
+                "Issue id",
+                "Issue title",
+                "Issue summary",
+                SEVERITY_LEVEL_INFORMATION,
+                "issue_type_id")
+
+        assertFailsWith(UnsupportedOperationException::class) {
+            safetySourceIssueBuilder.setNotificationBehavior(0)
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getIssueActionability_withDefaultBuilder_returnsManual() {
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+                .build()
+
+        assertThat(safetySourceIssue.issueActionability)
+            .isEqualTo(SafetySourceIssue.ISSUE_ACTIONABILITY_MANUAL)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getIssueActionability_whenSetExplicitly_returnsValueSet() {
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+                .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC)
+                .build()
+
+        assertThat(safetySourceIssue.issueActionability)
+            .isEqualTo(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC)
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = TIRAMISU)
+    fun getIssueActionability_withVersionLessThanU_throwsUnsupportedOperationException() {
+        // TODO(b/258228790): Remove after U is no longer in pre-release
+        assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+                .build()
+
+        assertFailsWith(UnsupportedOperationException::class) {
+            safetySourceIssue.issueActionability
+        }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun setIssueActionability_withInvalidIssueActionability_throwsIllegalArgumentException() {
+        val builder =
+            SafetySourceIssue.Builder(
+                "Issue id",
+                "Issue title",
+                "Issue summary",
+                SEVERITY_LEVEL_INFORMATION,
+                "issue_type_id")
+
+        val exception =
+            assertFailsWith(IllegalArgumentException::class) { builder.setIssueActionability(-1) }
+
+        assertThat(exception)
+            .hasMessageThat()
+            .isEqualTo("Unexpected IssueActionability for SafetySourceIssue: -1")
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = TIRAMISU)
+    fun setIssueActionability_withVersionLessThanU_throwsUnsupportedOperationException() {
+        // TODO(b/258228790): Remove after U is no longer in pre-release
+        assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
+        val safetySourceIssueBuilder =
+            SafetySourceIssue.Builder(
+                "Issue id",
+                "Issue title",
+                "Issue summary",
+                SEVERITY_LEVEL_INFORMATION,
+                "issue_type_id")
+
+        assertFailsWith(UnsupportedOperationException::class) {
+            safetySourceIssueBuilder.setIssueActionability(0)
+        }
+    }
+
+    @Test
     fun build_withNullId_throwsNullPointerException() {
         assertFailsWith(NullPointerException::class) {
             SafetySourceIssue.Builder(
@@ -500,14 +1090,38 @@
                 "Issue summary",
                 SEVERITY_LEVEL_INFORMATION,
                 "issue_type_id")
+
         val exception =
             assertFailsWith(IllegalArgumentException::class) { builder.setIssueCategory(-1) }
+
         assertThat(exception)
             .hasMessageThat()
             .isEqualTo("Unexpected IssueCategory for SafetySourceIssue: -1")
     }
 
     @Test
+    @SdkSuppress(maxSdkVersion = TIRAMISU)
+    fun build_withUIssueCategoryValueOnT_throwsIllegalArgumentException() {
+        // TODO(b/258228790): Remove after U is no longer in pre-release
+        assumeFalse(Build.VERSION.CODENAME == "UpsideDownCake")
+        val builder =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .addAction(action1)
+
+        val exception =
+            assertFailsWith(IllegalArgumentException::class) { builder.setIssueCategory(600) }
+
+        assertThat(exception)
+            .hasMessageThat()
+            .isEqualTo("Unexpected IssueCategory for SafetySourceIssue: 600")
+    }
+
+    @Test
     fun build_withInvalidOnDismissPendingIntent_throwsIllegalArgumentException() {
         val builder =
             SafetySourceIssue.Builder(
@@ -561,9 +1175,15 @@
 
         val exception =
             assertFailsWith(IllegalArgumentException::class) { safetySourceIssueBuilder.build() }
+
         assertThat(exception)
             .hasMessageThat()
-            .isEqualTo("Safety source issue must contain at least 1 action")
+            .isEqualTo(
+                if (SdkLevel.isAtLeastU()) {
+                    "Actionable safety source issue must contain at least 1 action"
+                } else {
+                    "Safety source issue must contain at least 1 action"
+                })
     }
 
     @Test
@@ -587,6 +1207,57 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun build_withNoActionsAndManualActionabilityOnU_throwsIllegalArgumentException() {
+        val safetySourceIssueBuilder =
+            SafetySourceIssue.Builder(
+                "Issue id",
+                "Issue title",
+                "Issue summary",
+                SEVERITY_LEVEL_INFORMATION,
+                "issue_type_id")
+
+        val exception =
+            assertFailsWith(IllegalArgumentException::class) { safetySourceIssueBuilder.build() }
+
+        assertThat(exception)
+            .hasMessageThat()
+            .isEqualTo("Actionable safety source issue must contain at least 1 action")
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun build_withNoActionsAndTipActionabilityOnU_success() {
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP)
+                .build()
+
+        assertThat(safetySourceIssue.actions).isEmpty()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun build_withNoActionsAndAutomaticActionabilityOnU_success() {
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC)
+                .build()
+
+        assertThat(safetySourceIssue.actions).isEmpty()
+    }
+
+    @Test
     fun describeContents_returns0() {
         val safetySourceIssue =
             SafetySourceIssue.Builder(
@@ -625,8 +1296,274 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun parcelRoundTrip_recreatesEqual_atLeastUpsideDownCake() {
+        val safetySourceIssue =
+            SafetySourceIssue.Builder(
+                    "Issue id",
+                    "Issue title",
+                    "Issue summary",
+                    SEVERITY_LEVEL_INFORMATION,
+                    "issue_type_id")
+                .setSubtitle("Issue subtitle")
+                .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA)
+                .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP)
+                .addAction(action1)
+                .addAction(action2)
+                .setOnDismissPendingIntent(pendingIntentService)
+                .setCustomNotification(
+                    Notification.Builder("Notification title", "Notification text")
+                        .addAction(action2)
+                        .build())
+                .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_DELAYED)
+                .setAttributionTitle("attribution title")
+                .setDeduplicationId("deduplication_id")
+                .build()
+
+        assertThat(safetySourceIssue).recreatesEqual(SafetySourceIssue.CREATOR)
+    }
+
+    @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        newTiramisuEqualsHashCodeToStringTester().test()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun equalsHashCodeToString_usingEqualsHashCodeToStringTester_atLeastUpsideDownCake() {
+        newUpsideDownCakeEqualsHashCodeToStringTester().test()
+    }
+
+    /**
+     * Creates a new [EqualsHashCodeToStringTester] instance with all the equality groups in the
+     * [newTiramisuEqualsHashCodeToStringTester] plus new equality groups covering all the new
+     * fields added in U.
+     */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    private fun newUpsideDownCakeEqualsHashCodeToStringTester() =
+        newTiramisuEqualsHashCodeToStringTester(
+                createCopyFromBuilder = { SafetySourceIssue.Builder(it).build() })
+            .addEqualityGroup(
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_INFORMATION,
+                        "issue_type_id")
+                    .setSubtitle("Issue subtitle")
+                    .setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
+                    .addAction(action1)
+                    .addAction(action2)
+                    .setOnDismissPendingIntent(pendingIntentService)
+                    .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_DELAYED)
+                    .setCustomNotification(
+                        Notification.Builder("Notification title", "Notification text")
+                            .addAction(action2)
+                            .build())
+                    .build())
+            .addEqualityGroup(
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_INFORMATION,
+                        "issue_type_id")
+                    .setSubtitle("Issue subtitle")
+                    .setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
+                    .addAction(action1)
+                    .addAction(action2)
+                    .setOnDismissPendingIntent(pendingIntentService)
+                    .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_IMMEDIATELY)
+                    .setCustomNotification(
+                        Notification.Builder("Other title", "Other text")
+                            .addAction(action2)
+                            .build())
+                    .build())
+            .addEqualityGroup(
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_INFORMATION,
+                        "issue_type_id")
+                    .setSubtitle("Issue subtitle")
+                    .setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
+                    .addAction(action1)
+                    .addAction(action2)
+                    .setOnDismissPendingIntent(pendingIntentService)
+                    .setAttributionTitle("attribution title")
+                    .build(),
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_INFORMATION,
+                        "issue_type_id")
+                    .setSubtitle("Issue subtitle")
+                    .setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
+                    .addAction(action1)
+                    .addAction(action2)
+                    .setOnDismissPendingIntent(pendingIntentService)
+                    .setAttributionTitle("attribution title")
+                    .build())
+            .addEqualityGroup(
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_CRITICAL_WARNING,
+                        "issue_type_id")
+                    .setAttributionTitle("Other issue attribution title")
+                    .addAction(action1)
+                    .build())
+            .addEqualityGroup(
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_INFORMATION,
+                        "issue_type_id")
+                    .setSubtitle("Issue subtitle")
+                    .setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
+                    .addAction(action1)
+                    .addAction(action2)
+                    .setOnDismissPendingIntent(pendingIntentService)
+                    .setAttributionTitle("attribution title")
+                    .setDeduplicationId("deduplication_id")
+                    .build(),
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_INFORMATION,
+                        "issue_type_id")
+                    .setSubtitle("Issue subtitle")
+                    .setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
+                    .addAction(action1)
+                    .addAction(action2)
+                    .setOnDismissPendingIntent(pendingIntentService)
+                    .setAttributionTitle("attribution title")
+                    .setDeduplicationId("deduplication_id")
+                    .build())
+            .addEqualityGroup(
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_INFORMATION,
+                        "issue_type_id")
+                    .setSubtitle("Issue subtitle")
+                    .setIssueCategory(ISSUE_CATEGORY_ACCOUNT)
+                    .addAction(action1)
+                    .addAction(action2)
+                    .setOnDismissPendingIntent(pendingIntentService)
+                    .setAttributionTitle("attribution title")
+                    .setDeduplicationId("other_deduplication_id")
+                    .build())
+            .addEqualityGroup(
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_INFORMATION,
+                        "issue_type_id")
+                    .setSubtitle("Other issue subtitle")
+                    .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DATA)
+                    .addAction(action1)
+                    .build())
+            .addEqualityGroup(
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_INFORMATION,
+                        "issue_type_id")
+                    .setSubtitle("Other issue subtitle")
+                    .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PASSWORDS)
+                    .addAction(action1)
+                    .build())
+            .addEqualityGroup(
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_INFORMATION,
+                        "issue_type_id")
+                    .setSubtitle("Other issue subtitle")
+                    .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_PERSONAL_SAFETY)
+                    .addAction(action1)
+                    .build())
+            .addEqualityGroup(
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_INFORMATION,
+                        "issue_type_id")
+                    .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_MANUAL)
+                    .addAction(action1)
+                    .setAttributionTitle("Attribution title")
+                    .setDeduplicationId("dedup_id")
+                    .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_NEVER)
+                    .build())
+            .addEqualityGroup(
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_INFORMATION,
+                        "issue_type_id")
+                    .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP)
+                    .addAction(action1)
+                    .build(),
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_INFORMATION,
+                        "issue_type_id")
+                    .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_TIP)
+                    .addAction(action1)
+                    .build())
+            .addEqualityGroup(
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_INFORMATION,
+                        "issue_type_id")
+                    .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC)
+                    .addAction(action1)
+                    .build())
+            .addEqualityGroup(
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_INFORMATION,
+                        "issue_type_id")
+                    .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC)
+                    .build())
+            .addEqualityGroup(
+                SafetySourceIssue.Builder(
+                        "Issue id",
+                        "Issue title",
+                        "Issue summary",
+                        SEVERITY_LEVEL_INFORMATION,
+                        "issue_type_id")
+                    .setNotificationBehavior(SafetySourceIssue.NOTIFICATION_BEHAVIOR_DELAYED)
+                    .setIssueActionability(SafetySourceIssue.ISSUE_ACTIONABILITY_AUTOMATIC)
+                    .build())
+
+    /**
+     * Creates a new [EqualsHashCodeToStringTester] instance which covers all the fields in the T
+     * API and is safe to use on any T+ API level.
+     */
+    private fun newTiramisuEqualsHashCodeToStringTester(
+        createCopyFromBuilder: ((SafetySourceIssue) -> SafetySourceIssue)? = null
+    ) =
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetySourceIssue.CREATOR, createCopy = createCopyFromBuilder)
             .addEqualityGroup(
                 SafetySourceIssue.Builder(
                         "Issue id",
@@ -683,7 +1620,7 @@
                 SafetySourceIssue.Builder(
                         "Issue id",
                         "Issue title",
-                        "Other issue summary",
+                        "Different issue summary",
                         SEVERITY_LEVEL_INFORMATION,
                         "issue_type_id")
                     .addAction(action1)
@@ -769,6 +1706,39 @@
                         PendingIntent.getService(
                             context, 0, Intent("Other PendingIntent service"), FLAG_IMMUTABLE))
                     .build())
-            .test()
-    }
+
+    private fun actionNewTiramisuEqualsHashCodeToStringTester(
+        createCopyFromBuilder: ((Action) -> Action)? = null
+    ) =
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = Action.CREATOR, createCopy = createCopyFromBuilder)
+            .addEqualityGroup(
+                Action.Builder("action_id", "Action label", pendingIntent1).build(),
+                Action.Builder("action_id", "Action label", pendingIntent1).build(),
+                Action.Builder("action_id", "Action label", pendingIntent1)
+                    .setWillResolve(false)
+                    .build())
+            .addEqualityGroup(
+                Action.Builder("action_id", "Action label", pendingIntent1)
+                    .setSuccessMessage("Action successfully completed")
+                    .build())
+            .addEqualityGroup(
+                Action.Builder("action_id", "Other action label", pendingIntent1).build())
+            .addEqualityGroup(
+                Action.Builder("other_action_id", "Action label", pendingIntent1).build())
+            .addEqualityGroup(
+                Action.Builder("action_id", "Action label", pendingIntent1)
+                    .setWillResolve(true)
+                    .build())
+            .addEqualityGroup(
+                Action.Builder(
+                        "action_id",
+                        "Action label",
+                        PendingIntent.getActivity(
+                            context, 0, Intent("Other action PendingIntent"), FLAG_IMMUTABLE))
+                    .build())
+            .addEqualityGroup(
+                Action.Builder("action_id", "Action label", pendingIntent1)
+                    .setSuccessMessage("Other action successfully completed")
+                    .build())
 }
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt
index dbbd743..0c11f01 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetySourceStatusTest.kt
@@ -20,6 +20,7 @@
 import android.app.PendingIntent.FLAG_IMMUTABLE
 import android.content.Context
 import android.content.Intent
+import android.os.Build
 import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING
 import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_INFORMATION
 import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED
@@ -31,6 +32,7 @@
 import androidx.test.core.app.ApplicationProvider.getApplicationContext
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.ext.truth.os.ParcelableSubject.assertThat
+import androidx.test.filters.SdkSuppress
 import com.android.permission.testing.EqualsHashCodeToStringTester
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.assertFailsWith
@@ -93,7 +95,7 @@
 
     @Test
     fun iconAction_equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        EqualsHashCodeToStringTester.ofParcelable(parcelableCreator = IconAction.CREATOR)
             .addEqualityGroup(
                 IconAction(ICON_TYPE_GEAR, pendingIntent1),
                 IconAction(ICON_TYPE_GEAR, pendingIntent1))
@@ -272,7 +274,22 @@
 
     @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        newTiramisuEqualsHashCodeToStringTester().test()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun equalsHashCodeToString_usingEqualsHashCodeToStringTester_atLeastAndroidU() {
+        newTiramisuEqualsHashCodeToStringTester(
+                createCopyFromBuilder = { SafetySourceStatus.Builder(it).build() })
+            .test()
+    }
+
+    private fun newTiramisuEqualsHashCodeToStringTester(
+        createCopyFromBuilder: ((SafetySourceStatus) -> SafetySourceStatus)? = null
+    ) =
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetySourceStatus.CREATOR, createCopy = createCopyFromBuilder)
             .addEqualityGroup(
                 SafetySourceStatus.Builder(
                         "Status title", "Status summary", SEVERITY_LEVEL_INFORMATION)
@@ -317,6 +334,4 @@
                         "Status title", "Status summary", SEVERITY_LEVEL_UNSPECIFIED)
                     .setEnabled(false)
                     .build())
-            .test()
-    }
 }
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt
index 73aee4d..88f7dfc 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt
@@ -16,9 +16,11 @@
 
 package android.safetycenter.cts.config
 
+import android.os.Build
 import android.safetycenter.config.SafetyCenterConfig
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.ext.truth.os.ParcelableSubject.assertThat
+import androidx.test.filters.SdkSuppress
 import com.android.permission.testing.EqualsHashCodeToStringTester
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.assertFailsWith
@@ -32,7 +34,8 @@
     @Test
     fun getSafetySourcesGroups_returnsSafetySourcesGroups() {
         assertThat(BASE.safetySourcesGroups)
-            .containsExactly(SafetySourcesGroupTest.RIGID, SafetySourcesGroupTest.HIDDEN)
+            .containsExactly(
+                SafetySourcesGroupTest.STATELESS_INFERRED, SafetySourcesGroupTest.HIDDEN_INFERRED)
             .inOrder()
     }
 
@@ -41,7 +44,7 @@
         val sourcesGroups = BASE.safetySourcesGroups
 
         assertFailsWith(UnsupportedOperationException::class) {
-            sourcesGroups.add(SafetySourcesGroupTest.COLLAPSIBLE_WITH_SUMMARY)
+            sourcesGroups.add(SafetySourcesGroupTest.STATEFUL_INFERRED_WITH_SUMMARY)
         }
     }
 
@@ -49,15 +52,16 @@
     fun builder_addSafetySourcesGroup_doesNotMutatePreviouslyBuiltInstance() {
         val safetyCenterConfigBuilder =
             SafetyCenterConfig.Builder()
-                .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID)
-                .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN)
+                .addSafetySourcesGroup(SafetySourcesGroupTest.STATELESS_INFERRED)
+                .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN_INFERRED)
         val sourceGroups = safetyCenterConfigBuilder.build().safetySourcesGroups
 
         safetyCenterConfigBuilder.addSafetySourcesGroup(
-            SafetySourcesGroupTest.COLLAPSIBLE_WITH_SUMMARY)
+            SafetySourcesGroupTest.STATEFUL_INFERRED_WITH_SUMMARY)
 
         assertThat(sourceGroups)
-            .containsExactly(SafetySourcesGroupTest.RIGID, SafetySourcesGroupTest.HIDDEN)
+            .containsExactly(
+                SafetySourcesGroupTest.STATELESS_INFERRED, SafetySourcesGroupTest.HIDDEN_INFERRED)
             .inOrder()
     }
 
@@ -73,26 +77,39 @@
 
     @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        newTiramisuEqualsHashCodeToStringTester().test()
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun equalsHashCodeToString_usingEqualsHashCodeToStringTester_atLeastAndroidU() {
+        newTiramisuEqualsHashCodeToStringTester(
+                createCopyFromBuilder = { SafetyCenterConfig.Builder(it).build() })
+            .test()
+    }
+
+    private fun newTiramisuEqualsHashCodeToStringTester(
+        createCopyFromBuilder: ((SafetyCenterConfig) -> SafetyCenterConfig)? = null
+    ) =
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetyCenterConfig.CREATOR, createCopy = createCopyFromBuilder)
             .addEqualityGroup(
                 BASE,
                 SafetyCenterConfig.Builder()
-                    .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID)
-                    .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN)
+                    .addSafetySourcesGroup(SafetySourcesGroupTest.STATELESS_INFERRED)
+                    .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN_INFERRED)
                     .build())
             .addEqualityGroup(
                 SafetyCenterConfig.Builder()
-                    .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN)
-                    .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID)
+                    .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN_INFERRED)
+                    .addSafetySourcesGroup(SafetySourcesGroupTest.STATELESS_INFERRED)
                     .build())
-            .test()
-    }
 
     companion object {
         private val BASE =
             SafetyCenterConfig.Builder()
-                .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID)
-                .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN)
+                .addSafetySourcesGroup(SafetySourcesGroupTest.STATELESS_INFERRED)
+                .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN_INFERRED)
                 .build()
     }
 }
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 60c09fc..f177e1e 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt
@@ -17,9 +17,12 @@
 package android.safetycenter.cts.config
 
 import android.content.res.Resources
+import android.os.Build
 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.testing.EqualsHashCodeToStringTester
 import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertThrows
@@ -33,7 +36,7 @@
     @Test
     fun getType_returnsType() {
         assertThat(DYNAMIC_BAREBONE.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
-        assertThat(DYNAMIC_ALL_OPTIONAL.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+        assertThat(dynamicAllOptional().type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
         assertThat(DYNAMIC_HIDDEN.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
         assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.type)
             .isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
@@ -41,40 +44,54 @@
         assertThat(STATIC_BAREBONE.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_STATIC)
         assertThat(STATIC_ALL_OPTIONAL.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_STATIC)
         assertThat(ISSUE_ONLY_BAREBONE.type).isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY)
-        assertThat(ISSUE_ONLY_ALL_OPTIONAL.type)
+        assertThat(issueOnlyAllOptional().type)
             .isEqualTo(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY)
     }
 
     @Test
     fun getId_returnsId() {
         assertThat(DYNAMIC_BAREBONE.id).isEqualTo(DYNAMIC_BAREBONE_ID)
-        assertThat(DYNAMIC_ALL_OPTIONAL.id).isEqualTo(DYNAMIC_ALL_OPTIONAL_ID)
+        assertThat(dynamicAllOptional().id).isEqualTo(DYNAMIC_ALL_OPTIONAL_ID)
         assertThat(DYNAMIC_HIDDEN.id).isEqualTo(DYNAMIC_HIDDEN_ID)
         assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.id).isEqualTo(DYNAMIC_HIDDEN_WITH_SEARCH_ID)
         assertThat(DYNAMIC_DISABLED.id).isEqualTo(DYNAMIC_DISABLED_ID)
         assertThat(STATIC_BAREBONE.id).isEqualTo(STATIC_BAREBONE_ID)
         assertThat(STATIC_ALL_OPTIONAL.id).isEqualTo(STATIC_ALL_OPTIONAL_ID)
         assertThat(ISSUE_ONLY_BAREBONE.id).isEqualTo(ISSUE_ONLY_BAREBONE_ID)
-        assertThat(ISSUE_ONLY_ALL_OPTIONAL.id).isEqualTo(ISSUE_ONLY_ALL_OPTIONAL_ID)
+        assertThat(issueOnlyAllOptional().id).isEqualTo(ISSUE_ONLY_ALL_OPTIONAL_ID)
     }
 
     @Test
     fun getPackageName_returnsPackageNameOrThrows() {
         assertThat(DYNAMIC_BAREBONE.packageName).isEqualTo(PACKAGE_NAME)
-        assertThat(DYNAMIC_ALL_OPTIONAL.packageName).isEqualTo(PACKAGE_NAME)
+        assertThat(dynamicAllOptional().packageName).isEqualTo(PACKAGE_NAME)
         assertThat(DYNAMIC_HIDDEN.packageName).isEqualTo(PACKAGE_NAME)
         assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.packageName).isEqualTo(PACKAGE_NAME)
         assertThat(DYNAMIC_DISABLED.packageName).isEqualTo(PACKAGE_NAME)
         assertThrows(UnsupportedOperationException::class.java) { STATIC_BAREBONE.packageName }
         assertThrows(UnsupportedOperationException::class.java) { STATIC_ALL_OPTIONAL.packageName }
         assertThat(ISSUE_ONLY_BAREBONE.packageName).isEqualTo(PACKAGE_NAME)
-        assertThat(ISSUE_ONLY_ALL_OPTIONAL.packageName).isEqualTo(PACKAGE_NAME)
+        assertThat(issueOnlyAllOptional().packageName).isEqualTo(PACKAGE_NAME)
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun getOptionalPackageName_returnsPackageNameOrNull() {
+        assertThat(DYNAMIC_BAREBONE.optionalPackageName).isEqualTo(PACKAGE_NAME)
+        assertThat(dynamicAllOptional().optionalPackageName).isEqualTo(PACKAGE_NAME)
+        assertThat(DYNAMIC_HIDDEN.optionalPackageName).isEqualTo(PACKAGE_NAME)
+        assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.optionalPackageName).isEqualTo(PACKAGE_NAME)
+        assertThat(DYNAMIC_DISABLED.optionalPackageName).isEqualTo(PACKAGE_NAME)
+        assertThat(STATIC_BAREBONE.optionalPackageName).isNull()
+        assertThat(STATIC_ALL_OPTIONAL.optionalPackageName).isEqualTo(PACKAGE_NAME)
+        assertThat(ISSUE_ONLY_BAREBONE.optionalPackageName).isEqualTo(PACKAGE_NAME)
+        assertThat(issueOnlyAllOptional().optionalPackageName).isEqualTo(PACKAGE_NAME)
     }
 
     @Test
     fun getTitleResId_returnsTitleResIdOrThrows() {
         assertThat(DYNAMIC_BAREBONE.titleResId).isEqualTo(REFERENCE_RES_ID)
-        assertThat(DYNAMIC_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID)
+        assertThat(dynamicAllOptional().titleResId).isEqualTo(REFERENCE_RES_ID)
         assertThat(DYNAMIC_DISABLED.titleResId).isEqualTo(REFERENCE_RES_ID)
         assertThat(DYNAMIC_HIDDEN.titleResId).isEqualTo(Resources.ID_NULL)
         assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.titleResId).isEqualTo(REFERENCE_RES_ID)
@@ -82,7 +99,7 @@
         assertThat(STATIC_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID)
         assertThrows(UnsupportedOperationException::class.java) { ISSUE_ONLY_BAREBONE.titleResId }
         assertThrows(UnsupportedOperationException::class.java) {
-            ISSUE_ONLY_ALL_OPTIONAL.titleResId
+            issueOnlyAllOptional().titleResId
         }
     }
 
@@ -91,7 +108,7 @@
         assertThrows(UnsupportedOperationException::class.java) {
             DYNAMIC_BAREBONE.titleForWorkResId
         }
-        assertThat(DYNAMIC_ALL_OPTIONAL.titleForWorkResId).isEqualTo(REFERENCE_RES_ID)
+        assertThat(dynamicAllOptional().titleForWorkResId).isEqualTo(REFERENCE_RES_ID)
         assertThrows(UnsupportedOperationException::class.java) {
             DYNAMIC_DISABLED.titleForWorkResId
         }
@@ -105,14 +122,14 @@
             ISSUE_ONLY_BAREBONE.titleForWorkResId
         }
         assertThrows(UnsupportedOperationException::class.java) {
-            ISSUE_ONLY_ALL_OPTIONAL.titleForWorkResId
+            issueOnlyAllOptional().titleForWorkResId
         }
     }
 
     @Test
     fun getSummaryResId_returnsSummaryResIdOrThrows() {
         assertThat(DYNAMIC_BAREBONE.summaryResId).isEqualTo(REFERENCE_RES_ID)
-        assertThat(DYNAMIC_ALL_OPTIONAL.summaryResId).isEqualTo(REFERENCE_RES_ID)
+        assertThat(dynamicAllOptional().summaryResId).isEqualTo(REFERENCE_RES_ID)
         assertThat(DYNAMIC_DISABLED.summaryResId).isEqualTo(REFERENCE_RES_ID)
         assertThat(DYNAMIC_HIDDEN.summaryResId).isEqualTo(Resources.ID_NULL)
         assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.summaryResId).isEqualTo(REFERENCE_RES_ID)
@@ -120,14 +137,14 @@
         assertThat(STATIC_ALL_OPTIONAL.summaryResId).isEqualTo(REFERENCE_RES_ID)
         assertThrows(UnsupportedOperationException::class.java) { ISSUE_ONLY_BAREBONE.summaryResId }
         assertThrows(UnsupportedOperationException::class.java) {
-            ISSUE_ONLY_ALL_OPTIONAL.summaryResId
+            issueOnlyAllOptional().summaryResId
         }
     }
 
     @Test
     fun getIntentAction_returnsIntentActionOrThrows() {
         assertThat(DYNAMIC_BAREBONE.intentAction).isEqualTo(INTENT_ACTION)
-        assertThat(DYNAMIC_ALL_OPTIONAL.intentAction).isEqualTo(INTENT_ACTION)
+        assertThat(dynamicAllOptional().intentAction).isEqualTo(INTENT_ACTION)
         assertThat(DYNAMIC_DISABLED.intentAction).isNull()
         assertThat(DYNAMIC_HIDDEN.intentAction).isNull()
         assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.intentAction).isEqualTo(INTENT_ACTION)
@@ -135,28 +152,28 @@
         assertThat(STATIC_ALL_OPTIONAL.intentAction).isEqualTo(INTENT_ACTION)
         assertThrows(UnsupportedOperationException::class.java) { ISSUE_ONLY_BAREBONE.intentAction }
         assertThrows(UnsupportedOperationException::class.java) {
-            ISSUE_ONLY_ALL_OPTIONAL.intentAction
+            issueOnlyAllOptional().intentAction
         }
     }
 
     @Test
     fun getProfile_returnsProfile() {
         assertThat(DYNAMIC_BAREBONE.profile).isEqualTo(SafetySource.PROFILE_PRIMARY)
-        assertThat(DYNAMIC_ALL_OPTIONAL.profile).isEqualTo(SafetySource.PROFILE_ALL)
+        assertThat(dynamicAllOptional().profile).isEqualTo(SafetySource.PROFILE_ALL)
         assertThat(DYNAMIC_DISABLED.profile).isEqualTo(SafetySource.PROFILE_PRIMARY)
         assertThat(DYNAMIC_HIDDEN.profile).isEqualTo(SafetySource.PROFILE_ALL)
         assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.profile).isEqualTo(SafetySource.PROFILE_ALL)
         assertThat(STATIC_BAREBONE.profile).isEqualTo(SafetySource.PROFILE_PRIMARY)
         assertThat(STATIC_ALL_OPTIONAL.profile).isEqualTo(SafetySource.PROFILE_ALL)
         assertThat(ISSUE_ONLY_BAREBONE.profile).isEqualTo(SafetySource.PROFILE_PRIMARY)
-        assertThat(ISSUE_ONLY_ALL_OPTIONAL.profile).isEqualTo(SafetySource.PROFILE_ALL)
+        assertThat(issueOnlyAllOptional().profile).isEqualTo(SafetySource.PROFILE_ALL)
     }
 
     @Test
     fun getInitialDisplayState_returnsInitialDisplayStateOrThrows() {
         assertThat(DYNAMIC_BAREBONE.initialDisplayState)
             .isEqualTo(SafetySource.INITIAL_DISPLAY_STATE_ENABLED)
-        assertThat(DYNAMIC_ALL_OPTIONAL.initialDisplayState)
+        assertThat(dynamicAllOptional().initialDisplayState)
             .isEqualTo(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
         assertThat(DYNAMIC_DISABLED.initialDisplayState)
             .isEqualTo(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
@@ -174,14 +191,14 @@
             ISSUE_ONLY_BAREBONE.initialDisplayState
         }
         assertThrows(UnsupportedOperationException::class.java) {
-            ISSUE_ONLY_ALL_OPTIONAL.initialDisplayState
+            issueOnlyAllOptional().initialDisplayState
         }
     }
 
     @Test
     fun getMaxSeverityLevel_returnsMaxSeverityLevelOrThrows() {
         assertThat(DYNAMIC_BAREBONE.maxSeverityLevel).isEqualTo(Integer.MAX_VALUE)
-        assertThat(DYNAMIC_ALL_OPTIONAL.maxSeverityLevel).isEqualTo(MAX_SEVERITY_LEVEL)
+        assertThat(dynamicAllOptional().maxSeverityLevel).isEqualTo(MAX_SEVERITY_LEVEL)
         assertThat(DYNAMIC_DISABLED.maxSeverityLevel).isEqualTo(Integer.MAX_VALUE)
         assertThat(DYNAMIC_HIDDEN.maxSeverityLevel).isEqualTo(Integer.MAX_VALUE)
         assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.maxSeverityLevel).isEqualTo(Integer.MAX_VALUE)
@@ -190,13 +207,13 @@
             STATIC_ALL_OPTIONAL.maxSeverityLevel
         }
         assertThat(ISSUE_ONLY_BAREBONE.maxSeverityLevel).isEqualTo(Integer.MAX_VALUE)
-        assertThat(ISSUE_ONLY_ALL_OPTIONAL.maxSeverityLevel).isEqualTo(MAX_SEVERITY_LEVEL)
+        assertThat(issueOnlyAllOptional().maxSeverityLevel).isEqualTo(MAX_SEVERITY_LEVEL)
     }
 
     @Test
     fun getSearchTermsResId_returnsSearchTermsResIdOrThrows() {
         assertThat(DYNAMIC_BAREBONE.searchTermsResId).isEqualTo(Resources.ID_NULL)
-        assertThat(DYNAMIC_ALL_OPTIONAL.searchTermsResId).isEqualTo(REFERENCE_RES_ID)
+        assertThat(dynamicAllOptional().searchTermsResId).isEqualTo(REFERENCE_RES_ID)
         assertThat(DYNAMIC_DISABLED.searchTermsResId).isEqualTo(Resources.ID_NULL)
         assertThat(DYNAMIC_HIDDEN.searchTermsResId).isEqualTo(Resources.ID_NULL)
         assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.searchTermsResId).isEqualTo(REFERENCE_RES_ID)
@@ -206,14 +223,14 @@
             ISSUE_ONLY_BAREBONE.searchTermsResId
         }
         assertThrows(UnsupportedOperationException::class.java) {
-            ISSUE_ONLY_ALL_OPTIONAL.searchTermsResId
+            issueOnlyAllOptional().searchTermsResId
         }
     }
 
     @Test
     fun isLoggingAllowed_returnsLoggingAllowedOrThrows() {
         assertThat(DYNAMIC_BAREBONE.isLoggingAllowed).isEqualTo(true)
-        assertThat(DYNAMIC_ALL_OPTIONAL.isLoggingAllowed).isEqualTo(false)
+        assertThat(dynamicAllOptional().isLoggingAllowed).isEqualTo(false)
         assertThat(DYNAMIC_DISABLED.isLoggingAllowed).isEqualTo(true)
         assertThat(DYNAMIC_HIDDEN.isLoggingAllowed).isEqualTo(true)
         assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.isLoggingAllowed).isEqualTo(true)
@@ -222,13 +239,13 @@
             STATIC_ALL_OPTIONAL.isLoggingAllowed
         }
         assertThat(ISSUE_ONLY_BAREBONE.isLoggingAllowed).isEqualTo(true)
-        assertThat(ISSUE_ONLY_ALL_OPTIONAL.isLoggingAllowed).isEqualTo(false)
+        assertThat(issueOnlyAllOptional().isLoggingAllowed).isEqualTo(false)
     }
 
     @Test
     fun isRefreshOnPageOpenAllowed_returnsRefreshOnPageOpenAllowedOrThrows() {
         assertThat(DYNAMIC_BAREBONE.isRefreshOnPageOpenAllowed).isEqualTo(false)
-        assertThat(DYNAMIC_ALL_OPTIONAL.isRefreshOnPageOpenAllowed).isEqualTo(true)
+        assertThat(dynamicAllOptional().isRefreshOnPageOpenAllowed).isEqualTo(true)
         assertThat(DYNAMIC_DISABLED.isRefreshOnPageOpenAllowed).isEqualTo(false)
         assertThat(DYNAMIC_HIDDEN.isRefreshOnPageOpenAllowed).isEqualTo(false)
         assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.isRefreshOnPageOpenAllowed).isEqualTo(false)
@@ -239,41 +256,90 @@
             STATIC_ALL_OPTIONAL.isRefreshOnPageOpenAllowed
         }
         assertThat(ISSUE_ONLY_BAREBONE.isRefreshOnPageOpenAllowed).isEqualTo(false)
-        assertThat(ISSUE_ONLY_ALL_OPTIONAL.isRefreshOnPageOpenAllowed).isEqualTo(true)
+        assertThat(issueOnlyAllOptional().isRefreshOnPageOpenAllowed).isEqualTo(true)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    @Test
+    fun areNotificationsAllowed_returnsNotificationsAllowed() {
+        assertThat(DYNAMIC_BAREBONE.areNotificationsAllowed()).isFalse()
+        assertThat(dynamicAllOptional().areNotificationsAllowed()).isTrue()
+        assertThat(DYNAMIC_DISABLED.areNotificationsAllowed()).isFalse()
+        assertThat(DYNAMIC_HIDDEN.areNotificationsAllowed()).isFalse()
+        assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.areNotificationsAllowed()).isFalse()
+        assertThat(STATIC_BAREBONE.areNotificationsAllowed()).isFalse()
+        assertThat(STATIC_ALL_OPTIONAL.areNotificationsAllowed()).isFalse()
+        assertThat(ISSUE_ONLY_BAREBONE.areNotificationsAllowed()).isFalse()
+        assertThat(issueOnlyAllOptional().areNotificationsAllowed()).isTrue()
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    @Test
+    fun getDeduplicationGroupsList_returnsDeduplicationGroups() {
+        assertThat(DYNAMIC_BAREBONE.deduplicationGroup).isNull()
+        assertThat(dynamicAllOptional().deduplicationGroup).isEqualTo(DEDUPLICATION_GROUP)
+        assertThat(DYNAMIC_DISABLED.deduplicationGroup).isNull()
+        assertThat(DYNAMIC_HIDDEN.deduplicationGroup).isNull()
+        assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.deduplicationGroup).isNull()
+        assertThat(STATIC_BAREBONE.deduplicationGroup).isNull()
+        assertThat(STATIC_ALL_OPTIONAL.deduplicationGroup).isNull()
+        assertThat(ISSUE_ONLY_BAREBONE.deduplicationGroup).isNull()
+        assertThat(issueOnlyAllOptional().deduplicationGroup).isEqualTo(DEDUPLICATION_GROUP)
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    @Test
+    fun getPackageCertificateHashes_returnsPackageCerts() {
+        assertThat(DYNAMIC_BAREBONE.packageCertificateHashes).isEmpty()
+        assertThat(dynamicAllOptional().packageCertificateHashes).containsExactly(HASH1)
+        assertThat(DYNAMIC_DISABLED.packageCertificateHashes).isEmpty()
+        assertThat(DYNAMIC_HIDDEN.packageCertificateHashes).isEmpty()
+        assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.packageCertificateHashes).isEmpty()
+        assertThat(STATIC_BAREBONE.packageCertificateHashes).isEmpty()
+        assertThat(STATIC_ALL_OPTIONAL.packageCertificateHashes).isEmpty()
+        assertThat(ISSUE_ONLY_BAREBONE.packageCertificateHashes).isEmpty()
+        assertThat(issueOnlyAllOptional().packageCertificateHashes).containsExactly(HASH1, HASH2)
     }
 
     @Test
     fun describeContents_returns0() {
         assertThat(DYNAMIC_BAREBONE.describeContents()).isEqualTo(0)
-        assertThat(DYNAMIC_ALL_OPTIONAL.describeContents()).isEqualTo(0)
+        assertThat(dynamicAllOptional().describeContents()).isEqualTo(0)
         assertThat(DYNAMIC_DISABLED.describeContents()).isEqualTo(0)
         assertThat(DYNAMIC_HIDDEN.describeContents()).isEqualTo(0)
         assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.describeContents()).isEqualTo(0)
         assertThat(STATIC_BAREBONE.describeContents()).isEqualTo(0)
         assertThat(STATIC_ALL_OPTIONAL.describeContents()).isEqualTo(0)
         assertThat(ISSUE_ONLY_BAREBONE.describeContents()).isEqualTo(0)
-        assertThat(ISSUE_ONLY_ALL_OPTIONAL.describeContents()).isEqualTo(0)
+        assertThat(issueOnlyAllOptional().describeContents()).isEqualTo(0)
     }
 
     @Test
     fun parcelRoundTrip_recreatesEqual() {
         assertThat(DYNAMIC_BAREBONE).recreatesEqual(SafetySource.CREATOR)
-        assertThat(DYNAMIC_ALL_OPTIONAL).recreatesEqual(SafetySource.CREATOR)
+        assertThat(dynamicAllOptional()).recreatesEqual(SafetySource.CREATOR)
         assertThat(DYNAMIC_DISABLED).recreatesEqual(SafetySource.CREATOR)
         assertThat(DYNAMIC_HIDDEN).recreatesEqual(SafetySource.CREATOR)
         assertThat(DYNAMIC_HIDDEN_WITH_SEARCH).recreatesEqual(SafetySource.CREATOR)
         assertThat(STATIC_BAREBONE).recreatesEqual(SafetySource.CREATOR)
         assertThat(STATIC_ALL_OPTIONAL).recreatesEqual(SafetySource.CREATOR)
         assertThat(ISSUE_ONLY_BAREBONE).recreatesEqual(SafetySource.CREATOR)
-        assertThat(ISSUE_ONLY_ALL_OPTIONAL).recreatesEqual(SafetySource.CREATOR)
+        assertThat(issueOnlyAllOptional()).recreatesEqual(SafetySource.CREATOR)
     }
 
     @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetySource.CREATOR,
+                createCopy =
+                    if (SdkLevel.isAtLeastU()) {
+                        { SafetySource.Builder(it).build() }
+                    } else {
+                        null
+                    })
             .addEqualityGroup(DYNAMIC_BAREBONE)
             .addEqualityGroup(
-                DYNAMIC_ALL_OPTIONAL,
+                dynamicAllOptional(),
                 SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
                     .setId(DYNAMIC_ALL_OPTIONAL_ID)
                     .setPackageName(PACKAGE_NAME)
@@ -287,6 +353,13 @@
                     .setSearchTermsResId(REFERENCE_RES_ID)
                     .setLoggingAllowed(false)
                     .setRefreshOnPageOpenAllowed(true)
+                    .apply {
+                        if (SdkLevel.isAtLeastU()) {
+                            setNotificationsAllowed(true)
+                            setDeduplicationGroup(DEDUPLICATION_GROUP)
+                            addPackageCertificateHash(HASH1)
+                        }
+                    }
                     .build())
             .addEqualityGroup(DYNAMIC_HIDDEN)
             .addEqualityGroup(DYNAMIC_HIDDEN_WITH_SEARCH)
@@ -294,7 +367,7 @@
             .addEqualityGroup(STATIC_BAREBONE)
             .addEqualityGroup(STATIC_ALL_OPTIONAL)
             .addEqualityGroup(ISSUE_ONLY_BAREBONE)
-            .addEqualityGroup(ISSUE_ONLY_ALL_OPTIONAL)
+            .addEqualityGroup(issueOnlyAllOptional())
             .addEqualityGroup(
                 SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
                     .setId("other")
@@ -309,6 +382,13 @@
                     .setSearchTermsResId(REFERENCE_RES_ID)
                     .setLoggingAllowed(false)
                     .setRefreshOnPageOpenAllowed(true)
+                    .apply {
+                        if (SdkLevel.isAtLeastU()) {
+                            setNotificationsAllowed(true)
+                            setDeduplicationGroup(DEDUPLICATION_GROUP)
+                            addPackageCertificateHash(HASH1)
+                        }
+                    }
                     .build())
             .addEqualityGroup(
                 SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
@@ -324,6 +404,13 @@
                     .setSearchTermsResId(REFERENCE_RES_ID)
                     .setLoggingAllowed(false)
                     .setRefreshOnPageOpenAllowed(true)
+                    .apply {
+                        if (SdkLevel.isAtLeastU()) {
+                            setNotificationsAllowed(true)
+                            setDeduplicationGroup(DEDUPLICATION_GROUP)
+                            addPackageCertificateHash(HASH1)
+                        }
+                    }
                     .build())
             .addEqualityGroup(
                 SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
@@ -339,6 +426,13 @@
                     .setSearchTermsResId(REFERENCE_RES_ID)
                     .setLoggingAllowed(false)
                     .setRefreshOnPageOpenAllowed(true)
+                    .apply {
+                        if (SdkLevel.isAtLeastU()) {
+                            setNotificationsAllowed(true)
+                            setDeduplicationGroup(DEDUPLICATION_GROUP)
+                            addPackageCertificateHash(HASH1)
+                        }
+                    }
                     .build())
             .addEqualityGroup(
                 SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
@@ -354,6 +448,13 @@
                     .setSearchTermsResId(REFERENCE_RES_ID)
                     .setLoggingAllowed(false)
                     .setRefreshOnPageOpenAllowed(true)
+                    .apply {
+                        if (SdkLevel.isAtLeastU()) {
+                            setNotificationsAllowed(true)
+                            setDeduplicationGroup(DEDUPLICATION_GROUP)
+                            addPackageCertificateHash(HASH1)
+                        }
+                    }
                     .build())
             .addEqualityGroup(
                 SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
@@ -369,6 +470,13 @@
                     .setSearchTermsResId(REFERENCE_RES_ID)
                     .setLoggingAllowed(false)
                     .setRefreshOnPageOpenAllowed(true)
+                    .apply {
+                        if (SdkLevel.isAtLeastU()) {
+                            setNotificationsAllowed(true)
+                            setDeduplicationGroup(DEDUPLICATION_GROUP)
+                            addPackageCertificateHash(HASH1)
+                        }
+                    }
                     .build())
             .addEqualityGroup(
                 SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
@@ -384,6 +492,13 @@
                     .setSearchTermsResId(REFERENCE_RES_ID)
                     .setLoggingAllowed(false)
                     .setRefreshOnPageOpenAllowed(true)
+                    .apply {
+                        if (SdkLevel.isAtLeastU()) {
+                            setNotificationsAllowed(true)
+                            setDeduplicationGroup(DEDUPLICATION_GROUP)
+                            addPackageCertificateHash(HASH1)
+                        }
+                    }
                     .build())
             .addEqualityGroup(
                 SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
@@ -406,6 +521,13 @@
                     .setSearchTermsResId(REFERENCE_RES_ID)
                     .setLoggingAllowed(false)
                     .setRefreshOnPageOpenAllowed(true)
+                    .apply {
+                        if (SdkLevel.isAtLeastU()) {
+                            setNotificationsAllowed(true)
+                            setDeduplicationGroup(DEDUPLICATION_GROUP)
+                            addPackageCertificateHash(HASH1)
+                        }
+                    }
                     .build())
             .addEqualityGroup(
                 SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
@@ -421,6 +543,13 @@
                     .setSearchTermsResId(REFERENCE_RES_ID)
                     .setLoggingAllowed(false)
                     .setRefreshOnPageOpenAllowed(true)
+                    .apply {
+                        if (SdkLevel.isAtLeastU()) {
+                            setNotificationsAllowed(true)
+                            setDeduplicationGroup(DEDUPLICATION_GROUP)
+                            addPackageCertificateHash(HASH1)
+                        }
+                    }
                     .build())
             .addEqualityGroup(
                 SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
@@ -436,6 +565,13 @@
                     .setSearchTermsResId(-1)
                     .setLoggingAllowed(false)
                     .setRefreshOnPageOpenAllowed(true)
+                    .apply {
+                        if (SdkLevel.isAtLeastU()) {
+                            setNotificationsAllowed(true)
+                            setDeduplicationGroup(DEDUPLICATION_GROUP)
+                            addPackageCertificateHash(HASH1)
+                        }
+                    }
                     .build())
             .addEqualityGroup(
                 SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
@@ -451,6 +587,13 @@
                     .setSearchTermsResId(REFERENCE_RES_ID)
                     .setLoggingAllowed(true)
                     .setRefreshOnPageOpenAllowed(true)
+                    .apply {
+                        if (SdkLevel.isAtLeastU()) {
+                            setNotificationsAllowed(true)
+                            setDeduplicationGroup(DEDUPLICATION_GROUP)
+                            addPackageCertificateHash(HASH1)
+                        }
+                    }
                     .build())
             .addEqualityGroup(
                 SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
@@ -466,7 +609,111 @@
                     .setSearchTermsResId(REFERENCE_RES_ID)
                     .setLoggingAllowed(false)
                     .setRefreshOnPageOpenAllowed(false)
+                    .apply {
+                        if (SdkLevel.isAtLeastU()) {
+                            setNotificationsAllowed(true)
+                            setDeduplicationGroup(DEDUPLICATION_GROUP)
+                            addPackageCertificateHash(HASH1)
+                        }
+                    }
                     .build())
+            .apply {
+                if (SdkLevel.isAtLeastU()) {
+                    addEqualityGroup(
+                        SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+                            .setId(DYNAMIC_ALL_OPTIONAL_ID)
+                            .setPackageName(PACKAGE_NAME)
+                            .setTitleResId(REFERENCE_RES_ID)
+                            .setTitleForWorkResId(REFERENCE_RES_ID)
+                            .setSummaryResId(REFERENCE_RES_ID)
+                            .setIntentAction(INTENT_ACTION)
+                            .setProfile(SafetySource.PROFILE_ALL)
+                            .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+                            .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+                            .setSearchTermsResId(REFERENCE_RES_ID)
+                            .setLoggingAllowed(false)
+                            .setRefreshOnPageOpenAllowed(true)
+                            .setNotificationsAllowed(false)
+                            .setDeduplicationGroup(DEDUPLICATION_GROUP)
+                            .addPackageCertificateHash(HASH1)
+                            .build())
+                    addEqualityGroup(
+                        SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+                            .setId(DYNAMIC_ALL_OPTIONAL_ID)
+                            .setPackageName(PACKAGE_NAME)
+                            .setTitleResId(REFERENCE_RES_ID)
+                            .setTitleForWorkResId(REFERENCE_RES_ID)
+                            .setSummaryResId(REFERENCE_RES_ID)
+                            .setIntentAction(INTENT_ACTION)
+                            .setProfile(SafetySource.PROFILE_ALL)
+                            .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+                            .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+                            .setSearchTermsResId(REFERENCE_RES_ID)
+                            .setLoggingAllowed(false)
+                            .setRefreshOnPageOpenAllowed(true)
+                            .setNotificationsAllowed(true)
+                            .setDeduplicationGroup("other_deduplication_group")
+                            .addPackageCertificateHash(HASH1)
+                            .build())
+                    // With no package cert hashes provided
+                    addEqualityGroup(
+                        SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+                            .setId(DYNAMIC_ALL_OPTIONAL_ID)
+                            .setPackageName(PACKAGE_NAME)
+                            .setTitleResId(REFERENCE_RES_ID)
+                            .setTitleForWorkResId(REFERENCE_RES_ID)
+                            .setSummaryResId(REFERENCE_RES_ID)
+                            .setIntentAction(INTENT_ACTION)
+                            .setProfile(SafetySource.PROFILE_ALL)
+                            .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+                            .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+                            .setSearchTermsResId(REFERENCE_RES_ID)
+                            .setLoggingAllowed(false)
+                            .setRefreshOnPageOpenAllowed(true)
+                            .setNotificationsAllowed(true)
+                            .setDeduplicationGroup(DEDUPLICATION_GROUP)
+                            .build())
+                    // With longer package cert hash list
+                    addEqualityGroup(
+                        SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+                            .setId(DYNAMIC_ALL_OPTIONAL_ID)
+                            .setPackageName(PACKAGE_NAME)
+                            .setTitleResId(REFERENCE_RES_ID)
+                            .setTitleForWorkResId(REFERENCE_RES_ID)
+                            .setSummaryResId(REFERENCE_RES_ID)
+                            .setIntentAction(INTENT_ACTION)
+                            .setProfile(SafetySource.PROFILE_ALL)
+                            .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+                            .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+                            .setSearchTermsResId(REFERENCE_RES_ID)
+                            .setLoggingAllowed(false)
+                            .setRefreshOnPageOpenAllowed(true)
+                            .setNotificationsAllowed(true)
+                            .setDeduplicationGroup(DEDUPLICATION_GROUP)
+                            .addPackageCertificateHash(HASH1)
+                            .addPackageCertificateHash(HASH2)
+                            .build())
+                    // With package cert hash list with different value
+                    addEqualityGroup(
+                        SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
+                            .setId(DYNAMIC_ALL_OPTIONAL_ID)
+                            .setPackageName(PACKAGE_NAME)
+                            .setTitleResId(REFERENCE_RES_ID)
+                            .setTitleForWorkResId(REFERENCE_RES_ID)
+                            .setSummaryResId(REFERENCE_RES_ID)
+                            .setIntentAction(INTENT_ACTION)
+                            .setProfile(SafetySource.PROFILE_ALL)
+                            .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED)
+                            .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
+                            .setSearchTermsResId(REFERENCE_RES_ID)
+                            .setLoggingAllowed(false)
+                            .setRefreshOnPageOpenAllowed(true)
+                            .setNotificationsAllowed(true)
+                            .setDeduplicationGroup(DEDUPLICATION_GROUP)
+                            .addPackageCertificateHash(HASH2)
+                            .build())
+                }
+            }
             .test()
     }
 
@@ -485,6 +732,9 @@
         private const val STATIC_ALL_OPTIONAL_ID = "static_all_optional"
         private const val ISSUE_ONLY_BAREBONE_ID = "issue_only_barebone"
         private const val ISSUE_ONLY_ALL_OPTIONAL_ID = "issue_only_all_optional"
+        private const val DEDUPLICATION_GROUP = "deduplication_group"
+        private const val HASH1 = "feed1"
+        private const val HASH2 = "feed2"
 
         internal val DYNAMIC_BAREBONE =
             SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
@@ -496,7 +746,7 @@
                 .setProfile(SafetySource.PROFILE_PRIMARY)
                 .build()
 
-        private val DYNAMIC_ALL_OPTIONAL =
+        private fun dynamicAllOptional(): SafetySource =
             SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC)
                 .setId(DYNAMIC_ALL_OPTIONAL_ID)
                 .setPackageName(PACKAGE_NAME)
@@ -510,6 +760,13 @@
                 .setSearchTermsResId(REFERENCE_RES_ID)
                 .setLoggingAllowed(false)
                 .setRefreshOnPageOpenAllowed(true)
+                .apply {
+                    if (SdkLevel.isAtLeastU()) {
+                        setNotificationsAllowed(true)
+                        setDeduplicationGroup(DEDUPLICATION_GROUP)
+                        addPackageCertificateHash(HASH1)
+                    }
+                }
                 .build()
 
         private val DYNAMIC_DISABLED =
@@ -554,6 +811,7 @@
         private val STATIC_ALL_OPTIONAL =
             SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC)
                 .setId(STATIC_ALL_OPTIONAL_ID)
+                .apply { if (SdkLevel.isAtLeastU()) setPackageName(PACKAGE_NAME) }
                 .setTitleResId(REFERENCE_RES_ID)
                 .setTitleForWorkResId(REFERENCE_RES_ID)
                 .setSummaryResId(REFERENCE_RES_ID)
@@ -569,7 +827,7 @@
                 .setProfile(SafetySource.PROFILE_PRIMARY)
                 .build()
 
-        private val ISSUE_ONLY_ALL_OPTIONAL =
+        private fun issueOnlyAllOptional(): SafetySource =
             SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY)
                 .setId(ISSUE_ONLY_ALL_OPTIONAL_ID)
                 .setPackageName(PACKAGE_NAME)
@@ -577,6 +835,14 @@
                 .setMaxSeverityLevel(MAX_SEVERITY_LEVEL)
                 .setLoggingAllowed(false)
                 .setRefreshOnPageOpenAllowed(true)
+                .apply {
+                    if (SdkLevel.isAtLeastU()) {
+                        setNotificationsAllowed(true)
+                        setDeduplicationGroup(DEDUPLICATION_GROUP)
+                        addPackageCertificateHash(HASH1)
+                        addPackageCertificateHash(HASH2)
+                    }
+                }
                 .build()
     }
 }
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt
index f742b5d..78f1f08 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt
@@ -17,9 +17,13 @@
 package android.safetycenter.cts.config
 
 import android.content.res.Resources
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
 import android.safetycenter.config.SafetySourcesGroup
+import androidx.annotation.RequiresApi
 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.testing.EqualsHashCodeToStringTester
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.assertFailsWith
@@ -32,78 +36,156 @@
 
     @Test
     fun getType_returnsType() {
-        assertThat(COLLAPSIBLE_WITH_SUMMARY.type)
-            .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE)
-        assertThat(COLLAPSIBLE_WITH_ICON.type)
-            .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE)
-        assertThat(COLLAPSIBLE_WITH_BOTH.type)
-            .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE)
-        assertThat(RIGID.type).isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID)
-        assertThat(HIDDEN.type).isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN)
+        assertThat(STATEFUL_INFERRED_WITH_SUMMARY.type)
+            .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL)
+        assertThat(STATEFUL_INFERRED_WITH_ICON.type)
+            .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL)
+        assertThat(STATEFUL_INFERRED_WITH_BOTH.type)
+            .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL)
+        assertThat(STATELESS_INFERRED.type)
+            .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS)
+        assertThat(HIDDEN_INFERRED.type)
+            .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN)
+        if (SdkLevel.isAtLeastU()) {
+            assertThat(STATEFUL_BAREBONE.type)
+                .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL)
+            assertThat(STATEFUL_ALL_OPTIONAL.type)
+                .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL)
+            assertThat(STATELESS_BAREBONE.type)
+                .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS)
+            assertThat(STATELESS_ALL_OPTIONAL.type)
+                .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS)
+            assertThat(HIDDEN_BAREBONE.type)
+                .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN)
+            assertThat(HIDDEN_ALL_OPTIONAL.type)
+                .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN)
+        }
     }
 
     @Test
     fun getId_returnsId() {
-        assertThat(COLLAPSIBLE_WITH_SUMMARY.id).isEqualTo(COLLAPSIBLE_WITH_SUMMARY_ID)
-        assertThat(COLLAPSIBLE_WITH_ICON.id).isEqualTo(COLLAPSIBLE_WITH_ICON_ID)
-        assertThat(COLLAPSIBLE_WITH_BOTH.id).isEqualTo(COLLAPSIBLE_WITH_BOTH_ID)
-        assertThat(RIGID.id).isEqualTo(RIGID_ID)
-        assertThat(HIDDEN.id).isEqualTo(HIDDEN_ID)
+        assertThat(STATEFUL_INFERRED_WITH_SUMMARY.id).isEqualTo(STATEFUL_INFERRED_WITH_SUMMARY_ID)
+        assertThat(STATEFUL_INFERRED_WITH_ICON.id).isEqualTo(STATEFUL_INFERRED_WITH_ICON_ID)
+        assertThat(STATEFUL_INFERRED_WITH_BOTH.id).isEqualTo(STATEFUL_INFERRED_WITH_BOTH_ID)
+        assertThat(STATELESS_INFERRED.id).isEqualTo(STATELESS_INFERRED_ID)
+        assertThat(HIDDEN_INFERRED.id).isEqualTo(HIDDEN_INFERRED_ID)
+        if (SdkLevel.isAtLeastU()) {
+            assertThat(STATEFUL_BAREBONE.id).isEqualTo(STATEFUL_BAREBONE_ID)
+            assertThat(STATEFUL_ALL_OPTIONAL.id).isEqualTo(STATEFUL_ALL_OPTIONAL_ID)
+            assertThat(STATELESS_BAREBONE.id).isEqualTo(STATELESS_BAREBONE_ID)
+            assertThat(STATELESS_ALL_OPTIONAL.id).isEqualTo(STATELESS_ALL_OPTIONAL_ID)
+            assertThat(HIDDEN_BAREBONE.id).isEqualTo(HIDDEN_BAREBONE_ID)
+            assertThat(HIDDEN_ALL_OPTIONAL.id).isEqualTo(HIDDEN_ALL_OPTIONAL_ID)
+        }
     }
 
     @Test
     fun getTitleResId_returnsTitleResId() {
-        assertThat(COLLAPSIBLE_WITH_SUMMARY.titleResId).isEqualTo(REFERENCE_RES_ID)
-        assertThat(COLLAPSIBLE_WITH_ICON.titleResId).isEqualTo(REFERENCE_RES_ID)
-        assertThat(COLLAPSIBLE_WITH_BOTH.titleResId).isEqualTo(REFERENCE_RES_ID)
-        assertThat(RIGID.titleResId).isEqualTo(REFERENCE_RES_ID)
+        assertThat(STATEFUL_INFERRED_WITH_SUMMARY.titleResId).isEqualTo(REFERENCE_RES_ID)
+        assertThat(STATEFUL_INFERRED_WITH_ICON.titleResId).isEqualTo(REFERENCE_RES_ID)
+        assertThat(STATEFUL_INFERRED_WITH_BOTH.titleResId).isEqualTo(REFERENCE_RES_ID)
+        assertThat(STATELESS_INFERRED.titleResId).isEqualTo(REFERENCE_RES_ID)
         // This is not an enforced invariant, titleResId should just be ignored for hidden groups
-        assertThat(HIDDEN.titleResId).isEqualTo(Resources.ID_NULL)
+        assertThat(HIDDEN_INFERRED.titleResId).isEqualTo(Resources.ID_NULL)
+        if (SdkLevel.isAtLeastU()) {
+            assertThat(STATEFUL_BAREBONE.titleResId).isEqualTo(REFERENCE_RES_ID)
+            assertThat(STATEFUL_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID)
+            assertThat(STATELESS_BAREBONE.titleResId).isEqualTo(REFERENCE_RES_ID)
+            assertThat(STATELESS_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID)
+            assertThat(HIDDEN_BAREBONE.titleResId).isEqualTo(Resources.ID_NULL)
+            assertThat(HIDDEN_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID)
+        }
     }
 
     @Test
     fun getSummaryResId_returnsSummaryResId() {
-        assertThat(COLLAPSIBLE_WITH_SUMMARY.summaryResId).isEqualTo(REFERENCE_RES_ID)
-        assertThat(COLLAPSIBLE_WITH_ICON.summaryResId).isEqualTo(Resources.ID_NULL)
-        assertThat(COLLAPSIBLE_WITH_BOTH.summaryResId).isEqualTo(REFERENCE_RES_ID)
-        assertThat(RIGID.summaryResId).isEqualTo(Resources.ID_NULL)
+        assertThat(STATEFUL_INFERRED_WITH_SUMMARY.summaryResId).isEqualTo(REFERENCE_RES_ID)
+        assertThat(STATEFUL_INFERRED_WITH_ICON.summaryResId).isEqualTo(Resources.ID_NULL)
+        assertThat(STATEFUL_INFERRED_WITH_BOTH.summaryResId).isEqualTo(REFERENCE_RES_ID)
+        assertThat(STATELESS_INFERRED.summaryResId).isEqualTo(Resources.ID_NULL)
         // This is not an enforced invariant, summaryResId should just be ignored for hidden groups
-        assertThat(HIDDEN.summaryResId).isEqualTo(Resources.ID_NULL)
+        assertThat(HIDDEN_INFERRED.summaryResId).isEqualTo(Resources.ID_NULL)
+        if (SdkLevel.isAtLeastU()) {
+            assertThat(STATEFUL_BAREBONE.titleResId).isEqualTo(REFERENCE_RES_ID)
+            assertThat(STATEFUL_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID)
+            assertThat(STATELESS_BAREBONE.titleResId).isEqualTo(REFERENCE_RES_ID)
+            assertThat(STATELESS_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID)
+            assertThat(HIDDEN_BAREBONE.titleResId).isEqualTo(Resources.ID_NULL)
+            assertThat(HIDDEN_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID)
+        }
     }
 
     @Test
     fun getStatelessIconType_returnsStatelessIconType() {
-        assertThat(COLLAPSIBLE_WITH_SUMMARY.statelessIconType)
+        assertThat(STATEFUL_INFERRED_WITH_SUMMARY.statelessIconType)
             .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE)
-        assertThat(COLLAPSIBLE_WITH_ICON.statelessIconType)
+        assertThat(STATEFUL_INFERRED_WITH_ICON.statelessIconType)
             .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
-        assertThat(COLLAPSIBLE_WITH_BOTH.statelessIconType)
+        assertThat(STATEFUL_INFERRED_WITH_BOTH.statelessIconType)
             .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
-        assertThat(RIGID.statelessIconType).isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE)
+        assertThat(STATELESS_INFERRED.statelessIconType)
+            .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE)
         // This is not an enforced invariant
         // statelessIconType should just be ignored for hidden groups
-        assertThat(HIDDEN.statelessIconType).isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE)
+        assertThat(HIDDEN_INFERRED.statelessIconType)
+            .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE)
+        if (SdkLevel.isAtLeastU()) {
+            assertThat(STATEFUL_BAREBONE.statelessIconType)
+                .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE)
+            assertThat(STATEFUL_ALL_OPTIONAL.statelessIconType)
+                .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+            assertThat(STATELESS_BAREBONE.statelessIconType)
+                .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE)
+            assertThat(STATELESS_ALL_OPTIONAL.statelessIconType)
+                .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+            assertThat(HIDDEN_BAREBONE.statelessIconType)
+                .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE)
+            assertThat(HIDDEN_ALL_OPTIONAL.statelessIconType)
+                .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+        }
     }
 
     @Test
     fun getSafetySources_returnsSafetySources() {
-        assertThat(COLLAPSIBLE_WITH_SUMMARY.safetySources)
+        assertThat(STATEFUL_INFERRED_WITH_SUMMARY.safetySources)
             .containsExactly(SafetySourceTest.DYNAMIC_BAREBONE)
-        assertThat(COLLAPSIBLE_WITH_ICON.safetySources)
+        assertThat(STATEFUL_INFERRED_WITH_ICON.safetySources)
             .containsExactly(SafetySourceTest.STATIC_BAREBONE)
-        assertThat(COLLAPSIBLE_WITH_BOTH.safetySources)
+        assertThat(STATEFUL_INFERRED_WITH_BOTH.safetySources)
             .containsExactly(
                 SafetySourceTest.DYNAMIC_BAREBONE,
                 SafetySourceTest.STATIC_BAREBONE,
                 SafetySourceTest.ISSUE_ONLY_BAREBONE)
             .inOrder()
-        assertThat(RIGID.safetySources).containsExactly(SafetySourceTest.STATIC_BAREBONE)
-        assertThat(HIDDEN.safetySources).containsExactly(SafetySourceTest.ISSUE_ONLY_BAREBONE)
+        assertThat(STATELESS_INFERRED.safetySources)
+            .containsExactly(SafetySourceTest.STATIC_BAREBONE)
+        assertThat(HIDDEN_INFERRED.safetySources)
+            .containsExactly(SafetySourceTest.ISSUE_ONLY_BAREBONE)
+        if (SdkLevel.isAtLeastU()) {
+            assertThat(STATEFUL_BAREBONE.safetySources)
+                .containsExactly(SafetySourceTest.DYNAMIC_BAREBONE)
+            assertThat(STATEFUL_ALL_OPTIONAL.safetySources)
+                .containsExactly(
+                    SafetySourceTest.DYNAMIC_BAREBONE,
+                    SafetySourceTest.STATIC_BAREBONE,
+                    SafetySourceTest.ISSUE_ONLY_BAREBONE)
+            assertThat(STATELESS_BAREBONE.safetySources)
+                .containsExactly(SafetySourceTest.STATIC_BAREBONE)
+            assertThat(STATELESS_ALL_OPTIONAL.safetySources)
+                .containsExactly(
+                    SafetySourceTest.DYNAMIC_BAREBONE,
+                    SafetySourceTest.STATIC_BAREBONE,
+                    SafetySourceTest.ISSUE_ONLY_BAREBONE)
+            assertThat(HIDDEN_BAREBONE.safetySources)
+                .containsExactly(SafetySourceTest.ISSUE_ONLY_BAREBONE)
+            assertThat(HIDDEN_ALL_OPTIONAL.safetySources)
+                .containsExactly(SafetySourceTest.ISSUE_ONLY_BAREBONE)
+        }
     }
 
     @Test
     fun getSafetySources_mutationsAreNotAllowed() {
-        val sources = COLLAPSIBLE_WITH_SUMMARY.safetySources
+        val sources = STATEFUL_INFERRED_WITH_SUMMARY.safetySources
 
         assertFailsWith(UnsupportedOperationException::class) {
             sources.add(SafetySourceTest.DYNAMIC_BAREBONE)
@@ -114,7 +196,7 @@
     fun builder_addSafetySource_doesNotMutatePreviouslyBuiltInstance() {
         val safetySourcesGroupBuilder =
             SafetySourcesGroup.Builder()
-                .setId(COLLAPSIBLE_WITH_SUMMARY_ID)
+                .setId(STATEFUL_INFERRED_WITH_SUMMARY_ID)
                 .setTitleResId(REFERENCE_RES_ID)
                 .setSummaryResId(REFERENCE_RES_ID)
                 .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE)
@@ -126,41 +208,141 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun build_hiddenGroupWithDynamicSource_throwsIllegalStateException() {
+        val builder =
+            SafetySourcesGroup.Builder()
+                .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN)
+                .setId(HIDDEN_BAREBONE_ID)
+                .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE)
+
+        val exception = assertFailsWith(IllegalStateException::class) { builder.build() }
+        assertThat(exception)
+            .hasMessageThat()
+            .isEqualTo(
+                "Safety sources groups of type hidden can only contain sources of type issue-only")
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun build_hiddenGroupWithStaticSource_throwsIllegalStateException() {
+        val builder =
+            SafetySourcesGroup.Builder()
+                .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN)
+                .setId(HIDDEN_BAREBONE_ID)
+                .addSafetySource(SafetySourceTest.STATIC_BAREBONE)
+
+        val exception = assertFailsWith(IllegalStateException::class) { builder.build() }
+        assertThat(exception)
+            .hasMessageThat()
+            .isEqualTo(
+                "Safety sources groups of type hidden can only contain sources of type issue-only")
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun build_statefulGroupWithIssueOnlySource_throwsIllegalStateException() {
+        val builder =
+            SafetySourcesGroup.Builder()
+                .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL)
+                .setId(STATEFUL_BAREBONE_ID)
+                .setTitleResId(REFERENCE_RES_ID)
+                .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE)
+
+        val exception = assertFailsWith(IllegalStateException::class) { builder.build() }
+        assertThat(exception)
+            .hasMessageThat()
+            .isEqualTo(
+                "Safety sources groups containing only sources of type issue-only must be of " +
+                    "type hidden")
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun build_statelessGroupWithIssueOnlySource_throwsIllegalStateException() {
+        val builder =
+            SafetySourcesGroup.Builder()
+                .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS)
+                .setId(STATELESS_BAREBONE_ID)
+                .setTitleResId(REFERENCE_RES_ID)
+                .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE)
+
+        val exception = assertFailsWith(IllegalStateException::class) { builder.build() }
+        assertThat(exception)
+            .hasMessageThat()
+            .isEqualTo(
+                "Safety sources groups containing only sources of type issue-only must be of " +
+                    "type hidden")
+    }
+
+    @Test
     fun describeContents_returns0() {
-        assertThat(COLLAPSIBLE_WITH_SUMMARY.describeContents()).isEqualTo(0)
-        assertThat(COLLAPSIBLE_WITH_ICON.describeContents()).isEqualTo(0)
-        assertThat(COLLAPSIBLE_WITH_BOTH.describeContents()).isEqualTo(0)
-        assertThat(RIGID.describeContents()).isEqualTo(0)
-        assertThat(HIDDEN.describeContents()).isEqualTo(0)
+        assertThat(STATEFUL_INFERRED_WITH_SUMMARY.describeContents()).isEqualTo(0)
+        assertThat(STATEFUL_INFERRED_WITH_ICON.describeContents()).isEqualTo(0)
+        assertThat(STATEFUL_INFERRED_WITH_BOTH.describeContents()).isEqualTo(0)
+        assertThat(STATELESS_INFERRED.describeContents()).isEqualTo(0)
+        assertThat(HIDDEN_INFERRED.describeContents()).isEqualTo(0)
+        if (SdkLevel.isAtLeastU()) {
+            assertThat(STATEFUL_BAREBONE.describeContents()).isEqualTo(0)
+            assertThat(STATEFUL_ALL_OPTIONAL.describeContents()).isEqualTo(0)
+            assertThat(STATELESS_BAREBONE.describeContents()).isEqualTo(0)
+            assertThat(STATELESS_ALL_OPTIONAL.describeContents()).isEqualTo(0)
+            assertThat(HIDDEN_BAREBONE.describeContents()).isEqualTo(0)
+            assertThat(HIDDEN_ALL_OPTIONAL.describeContents()).isEqualTo(0)
+        }
     }
 
     @Test
     fun parcelRoundTrip_recreatesEqual() {
-        assertThat(COLLAPSIBLE_WITH_SUMMARY).recreatesEqual(SafetySourcesGroup.CREATOR)
-        assertThat(COLLAPSIBLE_WITH_ICON).recreatesEqual(SafetySourcesGroup.CREATOR)
-        assertThat(COLLAPSIBLE_WITH_BOTH).recreatesEqual(SafetySourcesGroup.CREATOR)
-        assertThat(RIGID).recreatesEqual(SafetySourcesGroup.CREATOR)
-        assertThat(HIDDEN).recreatesEqual(SafetySourcesGroup.CREATOR)
+        assertThat(STATEFUL_INFERRED_WITH_SUMMARY).recreatesEqual(SafetySourcesGroup.CREATOR)
+        assertThat(STATEFUL_INFERRED_WITH_ICON).recreatesEqual(SafetySourcesGroup.CREATOR)
+        assertThat(STATEFUL_INFERRED_WITH_BOTH).recreatesEqual(SafetySourcesGroup.CREATOR)
+        assertThat(STATELESS_INFERRED).recreatesEqual(SafetySourcesGroup.CREATOR)
+        assertThat(HIDDEN_INFERRED).recreatesEqual(SafetySourcesGroup.CREATOR)
+        if (SdkLevel.isAtLeastU()) {
+            assertThat(STATEFUL_BAREBONE).recreatesEqual(SafetySourcesGroup.CREATOR)
+            assertThat(STATEFUL_ALL_OPTIONAL).recreatesEqual(SafetySourcesGroup.CREATOR)
+            assertThat(STATELESS_BAREBONE).recreatesEqual(SafetySourcesGroup.CREATOR)
+            assertThat(STATELESS_ALL_OPTIONAL).recreatesEqual(SafetySourcesGroup.CREATOR)
+            assertThat(HIDDEN_BAREBONE).recreatesEqual(SafetySourcesGroup.CREATOR)
+            assertThat(HIDDEN_ALL_OPTIONAL).recreatesEqual(SafetySourcesGroup.CREATOR)
+        }
     }
 
     @Test
     fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() {
-        EqualsHashCodeToStringTester()
-            .addEqualityGroup(COLLAPSIBLE_WITH_SUMMARY)
-            .addEqualityGroup(COLLAPSIBLE_WITH_ICON)
+        EqualsHashCodeToStringTester.ofParcelable(
+                parcelableCreator = SafetySourcesGroup.CREATOR,
+                createCopy =
+                    if (SdkLevel.isAtLeastU()) {
+                        { SafetySourcesGroup.Builder(it).build() }
+                    } else {
+                        null
+                    })
+            .addEqualityGroup(STATEFUL_INFERRED_WITH_SUMMARY)
+            .addEqualityGroup(STATEFUL_INFERRED_WITH_ICON)
             .addEqualityGroup(
-                COLLAPSIBLE_WITH_BOTH,
-                SafetySourcesGroup.Builder()
-                    .setId(COLLAPSIBLE_WITH_BOTH_ID)
-                    .setTitleResId(REFERENCE_RES_ID)
-                    .setSummaryResId(REFERENCE_RES_ID)
-                    .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
-                    .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE)
-                    .addSafetySource(SafetySourceTest.STATIC_BAREBONE)
-                    .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE)
-                    .build())
-            .addEqualityGroup(RIGID)
-            .addEqualityGroup(HIDDEN)
+                *mutableListOf(
+                        STATEFUL_INFERRED_WITH_BOTH,
+                        SafetySourcesGroup.Builder()
+                            .setId(STATEFUL_INFERRED_WITH_BOTH_ID)
+                            .setTitleResId(REFERENCE_RES_ID)
+                            .setSummaryResId(REFERENCE_RES_ID)
+                            .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+                            .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE)
+                            .addSafetySource(SafetySourceTest.STATIC_BAREBONE)
+                            .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE)
+                            .build())
+                    .apply { if (SdkLevel.isAtLeastU()) add(STATEFUL_ALL_OPTIONAL) }
+                    .toTypedArray())
+            .addEqualityGroup(
+                *mutableListOf(STATELESS_INFERRED)
+                    .apply { if (SdkLevel.isAtLeastU()) add(STATELESS_BAREBONE) }
+                    .toTypedArray())
+            .addEqualityGroup(
+                *mutableListOf(HIDDEN_INFERRED)
+                    .apply { if (SdkLevel.isAtLeastU()) add(HIDDEN_BAREBONE) }
+                    .toTypedArray())
             .addEqualityGroup(
                 SafetySourcesGroup.Builder()
                     .setId("other")
@@ -171,7 +353,7 @@
                     .build())
             .addEqualityGroup(
                 SafetySourcesGroup.Builder()
-                    .setId(COLLAPSIBLE_WITH_BOTH_ID)
+                    .setId(STATEFUL_INFERRED_WITH_BOTH_ID)
                     .setTitleResId(-1)
                     .setSummaryResId(REFERENCE_RES_ID)
                     .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
@@ -179,7 +361,7 @@
                     .build())
             .addEqualityGroup(
                 SafetySourcesGroup.Builder()
-                    .setId(COLLAPSIBLE_WITH_BOTH_ID)
+                    .setId(STATEFUL_INFERRED_WITH_BOTH_ID)
                     .setTitleResId(REFERENCE_RES_ID)
                     .setSummaryResId(-1)
                     .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
@@ -187,7 +369,7 @@
                     .build())
             .addEqualityGroup(
                 SafetySourcesGroup.Builder()
-                    .setId(COLLAPSIBLE_WITH_BOTH_ID)
+                    .setId(STATEFUL_INFERRED_WITH_BOTH_ID)
                     .setTitleResId(REFERENCE_RES_ID)
                     .setSummaryResId(REFERENCE_RES_ID)
                     .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE)
@@ -195,44 +377,57 @@
                     .build())
             .addEqualityGroup(
                 SafetySourcesGroup.Builder()
-                    .setId(COLLAPSIBLE_WITH_BOTH_ID)
+                    .setId(STATEFUL_INFERRED_WITH_BOTH_ID)
                     .setTitleResId(REFERENCE_RES_ID)
                     .setSummaryResId(REFERENCE_RES_ID)
                     .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
                     .addSafetySource(SafetySourceTest.STATIC_BAREBONE)
                     .build())
+            .apply {
+                if (SdkLevel.isAtLeastU()) {
+                    addEqualityGroup(STATEFUL_BAREBONE)
+                    addEqualityGroup(STATELESS_ALL_OPTIONAL)
+                    addEqualityGroup(HIDDEN_ALL_OPTIONAL)
+                }
+            }
             .test()
     }
 
     companion object {
         private const val REFERENCE_RES_ID = 9999
 
-        private const val COLLAPSIBLE_WITH_SUMMARY_ID = "collapsible_with_summary"
-        private const val COLLAPSIBLE_WITH_ICON_ID = "collapsible_with_icon"
-        private const val COLLAPSIBLE_WITH_BOTH_ID = "collapsible_with_both"
-        private const val RIGID_ID = "rigid"
-        private const val HIDDEN_ID = "hidden"
+        private const val STATEFUL_BAREBONE_ID = "stateful_barebone"
+        private const val STATEFUL_ALL_OPTIONAL_ID = "stateful_all_optional"
+        private const val STATELESS_BAREBONE_ID = "stateless_barebone"
+        private const val STATELESS_ALL_OPTIONAL_ID = "stateless_all_optional"
+        private const val HIDDEN_BAREBONE_ID = "hidden_barebone"
+        private const val HIDDEN_ALL_OPTIONAL_ID = "hidden_all_optional"
+        private const val STATEFUL_INFERRED_WITH_SUMMARY_ID = "stateful_inferred_with_summary"
+        private const val STATEFUL_INFERRED_WITH_ICON_ID = "stateful_inferred_with_icon"
+        private const val STATEFUL_INFERRED_WITH_BOTH_ID = STATEFUL_ALL_OPTIONAL_ID
+        private const val STATELESS_INFERRED_ID = STATELESS_BAREBONE_ID
+        private const val HIDDEN_INFERRED_ID = HIDDEN_BAREBONE_ID
 
         // TODO(b/230078826): Consider extracting shared constants to a separate file.
-        internal val COLLAPSIBLE_WITH_SUMMARY =
+        internal val STATEFUL_INFERRED_WITH_SUMMARY =
             SafetySourcesGroup.Builder()
-                .setId(COLLAPSIBLE_WITH_SUMMARY_ID)
+                .setId(STATEFUL_INFERRED_WITH_SUMMARY_ID)
                 .setTitleResId(REFERENCE_RES_ID)
                 .setSummaryResId(REFERENCE_RES_ID)
                 .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE)
                 .build()
 
-        private val COLLAPSIBLE_WITH_ICON =
+        private val STATEFUL_INFERRED_WITH_ICON =
             SafetySourcesGroup.Builder()
-                .setId(COLLAPSIBLE_WITH_ICON_ID)
+                .setId(STATEFUL_INFERRED_WITH_ICON_ID)
                 .setTitleResId(REFERENCE_RES_ID)
                 .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
                 .addSafetySource(SafetySourceTest.STATIC_BAREBONE)
                 .build()
 
-        private val COLLAPSIBLE_WITH_BOTH =
+        private val STATEFUL_INFERRED_WITH_BOTH =
             SafetySourcesGroup.Builder()
-                .setId(COLLAPSIBLE_WITH_BOTH_ID)
+                .setId(STATEFUL_INFERRED_WITH_BOTH_ID)
                 .setTitleResId(REFERENCE_RES_ID)
                 .setSummaryResId(REFERENCE_RES_ID)
                 .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
@@ -241,17 +436,86 @@
                 .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE)
                 .build()
 
-        internal val RIGID =
+        private val STATEFUL_BAREBONE: SafetySourcesGroup
+            @RequiresApi(UPSIDE_DOWN_CAKE)
+            get() =
+                SafetySourcesGroup.Builder()
+                    .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL)
+                    .setId(STATEFUL_BAREBONE_ID)
+                    .setTitleResId(REFERENCE_RES_ID)
+                    .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE)
+                    .build()
+
+        private val STATEFUL_ALL_OPTIONAL: SafetySourcesGroup
+            @RequiresApi(UPSIDE_DOWN_CAKE)
+            get() =
+                SafetySourcesGroup.Builder()
+                    .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATEFUL)
+                    .setId(STATEFUL_ALL_OPTIONAL_ID)
+                    .setTitleResId(REFERENCE_RES_ID)
+                    .setSummaryResId(REFERENCE_RES_ID)
+                    .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+                    .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE)
+                    .addSafetySource(SafetySourceTest.STATIC_BAREBONE)
+                    .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE)
+                    .build()
+
+        internal val STATELESS_INFERRED =
             SafetySourcesGroup.Builder()
-                .setId(RIGID_ID)
+                .setId(STATELESS_INFERRED_ID)
                 .setTitleResId(REFERENCE_RES_ID)
                 .addSafetySource(SafetySourceTest.STATIC_BAREBONE)
                 .build()
 
-        internal val HIDDEN =
+        private val STATELESS_BAREBONE: SafetySourcesGroup
+            @RequiresApi(UPSIDE_DOWN_CAKE)
+            get() =
+                SafetySourcesGroup.Builder()
+                    .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS)
+                    .setId(STATELESS_BAREBONE_ID)
+                    .setTitleResId(REFERENCE_RES_ID)
+                    .addSafetySource(SafetySourceTest.STATIC_BAREBONE)
+                    .build()
+
+        private val STATELESS_ALL_OPTIONAL: SafetySourcesGroup
+            @RequiresApi(UPSIDE_DOWN_CAKE)
+            get() =
+                SafetySourcesGroup.Builder()
+                    .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS)
+                    .setId(STATELESS_ALL_OPTIONAL_ID)
+                    .setTitleResId(REFERENCE_RES_ID)
+                    .setSummaryResId(REFERENCE_RES_ID)
+                    .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+                    .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE)
+                    .addSafetySource(SafetySourceTest.STATIC_BAREBONE)
+                    .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE)
+                    .build()
+
+        internal val HIDDEN_INFERRED =
             SafetySourcesGroup.Builder()
-                .setId(HIDDEN_ID)
+                .setId(HIDDEN_INFERRED_ID)
                 .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE)
                 .build()
+
+        private val HIDDEN_BAREBONE: SafetySourcesGroup
+            @RequiresApi(UPSIDE_DOWN_CAKE)
+            get() =
+                SafetySourcesGroup.Builder()
+                    .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN)
+                    .setId(HIDDEN_BAREBONE_ID)
+                    .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE)
+                    .build()
+
+        private val HIDDEN_ALL_OPTIONAL: SafetySourcesGroup
+            @RequiresApi(UPSIDE_DOWN_CAKE)
+            get() =
+                SafetySourcesGroup.Builder()
+                    .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN)
+                    .setId(HIDDEN_ALL_OPTIONAL_ID)
+                    .setTitleResId(REFERENCE_RES_ID)
+                    .setSummaryResId(REFERENCE_RES_ID)
+                    .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+                    .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE)
+                    .build()
     }
 }
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/CtsNotificationListener.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/CtsNotificationListener.kt
new file mode 100644
index 0000000..7c988d6
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/CtsNotificationListener.kt
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.safetycenter.cts.testing
+
+import android.content.ComponentName
+import android.os.ConditionVariable
+import android.safetycenter.cts.testing.Coroutines.TIMEOUT_LONG
+import android.safetycenter.cts.testing.Coroutines.TIMEOUT_SHORT
+import android.safetycenter.cts.testing.Coroutines.waitForWithTimeout
+import android.service.notification.NotificationListenerService
+import android.service.notification.StatusBarNotification
+import android.util.Log
+import com.android.compatibility.common.util.SystemUtil
+import com.google.common.truth.Truth.assertThat
+import java.time.Duration
+import java.util.concurrent.CopyOnWriteArrayList
+import java.util.concurrent.TimeoutException
+import kotlinx.coroutines.TimeoutCancellationException
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+
+/** Used in CTS tests to check whether expected notifications are present in the status bar. */
+class CtsNotificationListener : NotificationListenerService() {
+
+    private sealed class NotificationEvent(val statusBarNotification: StatusBarNotification)
+
+    private class NotificationPosted(statusBarNotification: StatusBarNotification) :
+        NotificationEvent(statusBarNotification) {
+        override fun toString(): String = "Posted $statusBarNotification"
+    }
+
+    private class NotificationRemoved(statusBarNotification: StatusBarNotification) :
+        NotificationEvent(statusBarNotification) {
+        override fun toString(): String = "Removed $statusBarNotification"
+    }
+
+    override fun onNotificationPosted(statusBarNotification: StatusBarNotification) {
+        super.onNotificationPosted(statusBarNotification)
+        if (isSafetyCenterNotification(statusBarNotification)) {
+            events.add(NotificationPosted(statusBarNotification))
+        }
+    }
+
+    override fun onNotificationRemoved(statusBarNotification: StatusBarNotification) {
+        super.onNotificationRemoved(statusBarNotification)
+        events.add(NotificationRemoved(statusBarNotification))
+    }
+
+    override fun onListenerConnected() {
+        Log.d(TAG, "onListenerConnected")
+        super.onListenerConnected()
+        instance = this
+        connected.open()
+    }
+
+    override fun onListenerDisconnected() {
+        Log.d(TAG, "onListenerDisconnected")
+        super.onListenerDisconnected()
+        connected.close()
+        instance = null
+    }
+
+    companion object {
+        private const val TAG = "CtsNotificationListener"
+
+        private val id: String =
+            "android.safetycenter.cts/" + CtsNotificationListener::class.java.name
+        private val componentName =
+            ComponentName("android.safetycenter.cts", CtsNotificationListener::class.java.name)
+
+        private val connected = ConditionVariable(false)
+        private var instance: CtsNotificationListener? = null
+
+        private val events = CopyOnWriteArrayList<NotificationEvent>()
+
+        /**
+         * Invokes the given [block] while observing all [NotificationEvent] of type [T] and returns
+         * those events.
+         *
+         * This method blocks until [duration] has elapsed and then returns a possibly-empty list
+         * containing those events.
+         */
+        private inline fun <reified T : NotificationEvent> getNotificationEvents(
+            duration: Duration,
+            block: () -> Any?
+        ): List<T> {
+            events.clear()
+            block.invoke()
+            return runBlocking {
+                // TODO(b/262691874): Refactor notification tests (reduce use of delays)
+                delay(duration.toMillis())
+                events.filterIsInstance<T>()
+            }
+        }
+
+        /**
+         * Invokes the given [block] while observing all [NotificationEvent] of type [T] and returns
+         * those events.
+         *
+         * This method blocks until either [limit] events of type [T] have occurred, or [duration]
+         * has elapsed and then returns a possibly-empty list containing those events.
+         */
+        private inline fun <reified T : NotificationEvent> getNotificationEvents(
+            limit: Int,
+            duration: Duration,
+            block: () -> Any?
+        ): List<T> {
+            require(limit > 0) { "limit must be greater than zero" }
+            events.clear()
+            block.invoke()
+            return runBlocking {
+                try {
+                    waitForWithTimeout(duration) { events.count { it is T } >= limit }
+                } catch (e: TimeoutCancellationException) {
+                    // OK, this means there weren't limit events within duration, continue
+                }
+                events.filterIsInstance<T>().take(limit)
+            }
+        }
+
+        /**
+         * Invokes the given [block] and asserts that no notifications are posted within [duration].
+         */
+        fun assertNoNotificationsPosted(duration: Duration = TIMEOUT_SHORT, block: () -> Any?) {
+            assertThat(getNextNotificationPostedOrNull(duration, block)).isNull()
+        }
+
+        /**
+         * Invokes the given [block] and asserts that a notification is posted within [duration].
+         */
+        fun assertAnyNotificationPosted(duration: Duration = TIMEOUT_LONG, block: () -> Any?) {
+            assertThat(getNextNotificationPostedOrNull(duration, block)).isNotNull()
+        }
+
+        /**
+         * Invokes the given [block] and then waits until the next notification is posted and
+         * returns it, or returns `null` if no notification is posted within [timeout].
+         */
+        fun getNextNotificationPostedOrNull(
+            timeout: Duration = TIMEOUT_LONG,
+            block: () -> Any?
+        ): StatusBarNotification? {
+            return getNotificationEvents<NotificationPosted>(limit = 1, duration = timeout, block)
+                .firstOrNull()
+                ?.statusBarNotification
+        }
+
+        /**
+         * Invoked the given [block] and then returns a possibly-empty list of all the
+         * [StatusBarNotification] instances that were posted within [duration].
+         */
+        fun getAllNotificationsPosted(
+            duration: Duration = TIMEOUT_SHORT,
+            block: () -> Any?
+        ): List<StatusBarNotification> {
+            return getNotificationEvents<NotificationPosted>(duration, block).map {
+                it.statusBarNotification
+            }
+        }
+
+        /**
+         * Invokes the given [block] and asserts that a notification is removed within [duration].
+         */
+        fun assertAnyNotificationRemoved(duration: Duration = TIMEOUT_LONG, block: () -> Any?) {
+            assertThat(getNextNotificationRemovedOrNull(duration, block)).isNotNull()
+        }
+
+        /**
+         * Invokes the given [block] and then waits until the next notification is removed and
+         * returns it, or returns `null` if no notification is removed within [timeout].
+         */
+        fun getNextNotificationRemovedOrNull(
+            timeout: Duration = TIMEOUT_LONG,
+            block: () -> Any?
+        ): StatusBarNotification? {
+            return getNotificationEvents<NotificationRemoved>(limit = 1, duration = timeout, block)
+                .firstOrNull()
+                ?.statusBarNotification
+        }
+
+        /**
+         * Cancels a specific notification and then waits for it to be removed by the notification
+         * manager, or throws if it has not been removed within [timeout].
+         */
+        fun cancelAndWait(key: String, timeout: Duration = TIMEOUT_LONG) {
+            val cancelled =
+                getNextNotificationRemovedOrNull(timeout) { instance?.cancelNotification(key) }
+            assertThat(cancelled).isNotNull()
+            assertThat(cancelled!!.key).isEqualTo(key)
+        }
+
+        /** Runs a shell command to allow or disallow the listener. Use before and after test. */
+        fun toggleListenerAccess(allowed: Boolean) {
+            // TODO(b/260335646): Try to do this using the AndroidTest.xml instead of in code
+            val verb = if (allowed) "allow" else "disallow"
+            SystemUtil.runShellCommand("cmd notification ${verb}_listener $id")
+            if (allowed) {
+                requestRebind(componentName)
+                if (!connected.block(TIMEOUT_LONG.toMillis())) {
+                    throw TimeoutException("Notification listener not connected")
+                }
+            }
+        }
+
+        private fun isSafetyCenterNotification(
+            statusBarNotification: StatusBarNotification
+        ): Boolean =
+            statusBarNotification.packageName == "android" &&
+                statusBarNotification.notification.channelId == "safety_center"
+    }
+}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/NotificationCharacteristics.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/NotificationCharacteristics.kt
new file mode 100644
index 0000000..2a5935a
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/NotificationCharacteristics.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.safetycenter.cts.testing
+
+import android.app.Notification
+import android.service.notification.StatusBarNotification
+import com.google.common.truth.Truth.assertThat
+
+/** The characteristic properties of a notification. */
+data class NotificationCharacteristics(val title: String, val text: String) {
+    companion object {
+        fun assertNotificationMatches(
+            statusBarNotification: StatusBarNotification,
+            expected: NotificationCharacteristics
+        ) {
+            statusBarNotification.notification?.apply {
+                assertThat(extras.getString(Notification.EXTRA_TITLE)).isEqualTo(expected.title)
+                assertThat(extras.getString(Notification.EXTRA_TEXT)).isEqualTo(expected.text)
+            }
+        }
+
+        fun assertNotificationsMatch(
+            statusBarNotifications: List<StatusBarNotification>,
+            vararg expected: NotificationCharacteristics
+        ) {
+            assertThat(statusBarNotifications).hasSize(expected.size)
+            statusBarNotifications.zip(expected).forEach { (sbn, characteristics) ->
+                assertNotificationMatches(sbn, characteristics)
+            }
+        }
+    }
+}
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterApisWithShellPermissions.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterApisWithShellPermissions.kt
index 4892e6c..da7238a 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterApisWithShellPermissions.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterApisWithShellPermissions.kt
@@ -80,9 +80,16 @@
      * Calls [SafetyCenterManager.refreshSafetySources] adopting Shell's [MANAGE_SAFETY_CENTER]
      * permission.
      */
-    fun SafetyCenterManager.refreshSafetySourcesWithPermission(refreshReason: Int) {
+    fun SafetyCenterManager.refreshSafetySourcesWithPermission(
+        refreshReason: Int,
+        safetySourceIds: List<String>? = null
+    ) {
         callWithShellPermissionIdentity(MANAGE_SAFETY_CENTER) {
-            refreshSafetySources(refreshReason)
+            if (safetySourceIds != null) {
+                refreshSafetySources(refreshReason, safetySourceIds)
+            } else {
+                refreshSafetySources(refreshReason)
+            }
         }
     }
 
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsConfigs.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsConfigs.kt
index 9eeb41f..77d114b 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsConfigs.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsConfigs.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.content.res.Resources
+import android.os.Build
 import android.safetycenter.SafetySourceData
 import android.safetycenter.config.SafetyCenterConfig
 import android.safetycenter.config.SafetySource
@@ -26,6 +27,8 @@
 import android.safetycenter.config.SafetySource.SAFETY_SOURCE_TYPE_STATIC
 import android.safetycenter.config.SafetySourcesGroup
 import android.safetycenter.cts.testing.SettingsPackage.getSettingsPackageName
+import androidx.annotation.RequiresApi
+import com.android.modules.utils.build.SdkLevel
 
 /**
  * A class that provides [SafetyCenterConfig] objects and associated constants to facilitate setting
@@ -77,6 +80,44 @@
             dynamicSafetySourceBuilder(SINGLE_SOURCE_ID).setMaxSeverityLevel(0).build())
 
     /**
+     * SHA256 hash of a package certificate.
+     *
+     * <p>This is a fake certificate, and can be used to test failure cases, or to test a list of
+     * certificates when only one match is required.
+     */
+    const val PACKAGE_CERT_HASH_FAKE = "feed12"
+
+    /** SHA256 hashes of the certificate(s) known to sign the CTS tests. */
+    private val PACKAGE_CERT_HASHES_CTS =
+        listOf(
+            "6cecc50e34ae31bfb5678986d6d6d3736c571ded2f2459527793e1f054eb0c9b",
+            "a40da80a59d170caa950cf15c18c454d47a39b26989d8b640ecd745ba71bf5dc")
+
+    /** An invalid SHA256 hash (not a byte string, not even number of chars). */
+    const val PACKAGE_CERT_HASH_INVALID = "0124ppl"
+
+    /** A simple [SafetyCenterConfig] for CTS tests with a fake/incorrect package cert hash. */
+    val SINGLE_SOURCE_WITH_FAKE_CERT: SafetyCenterConfig
+        @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        get() =
+            singleSourceConfig(
+                dynamicSafetySourceBuilder(SINGLE_SOURCE_ID)
+                    .addPackageCertificateHash(PACKAGE_CERT_HASH_FAKE)
+                    .build())
+
+    /**
+     * A simple [SafetyCenterConfig] for CTS tests with a invalid package cert hash (not a
+     * hex-formatted byte string).
+     */
+    val SINGLE_SOURCE_WITH_INVALID_CERT: SafetyCenterConfig
+        @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        get() =
+            singleSourceConfig(
+                dynamicSafetySourceBuilder(SINGLE_SOURCE_ID)
+                    .addPackageCertificateHash(PACKAGE_CERT_HASH_INVALID)
+                    .build())
+
+    /**
      * A simple [SafetyCenterConfig] for CTS tests with a source that does not support refresh on
      * page open.
      */
@@ -126,7 +167,7 @@
     /**
      * ID of a [SafetySourcesGroup] provided by [SUMMARY_TEST_GROUP_CONFIG], containing sources:
      * [SOURCE_ID_1], [SOURCE_ID_2], [SOURCE_ID_3], [SOURCE_ID_4], [SOURCE_ID_5], [SOURCE_ID_6],
-     * [SOURCE_ID_7], [STATIC_IN_COLLAPSIBLE_ID].
+     * [SOURCE_ID_7], [STATIC_IN_STATEFUL_ID].
      */
     const val SUMMARY_TEST_GROUP_ID = "summary_test_group_id"
 
@@ -153,15 +194,16 @@
 
     /**
      * ID of a [SafetySourcesGroup] provided by [COMPLEX_CONFIG], containing sources:
-     * [DYNAMIC_IN_COLLAPSIBLE_ID], [STATIC_IN_COLLAPSIBLE_ID].
+     * [DYNAMIC_IN_STATEFUL_ID], [STATIC_IN_STATEFUL_ID].
      */
-    const val MIXED_COLLAPSIBLE_GROUP_ID = "mixed_collapsible"
+    const val MIXED_STATEFUL_GROUP_ID = "mixed_stateful"
 
     /**
      * ID of a [SafetySourcesGroup] provided by [COMPLEX_CONFIG] and [COMPLEX_ALL_PROFILE_CONFIG],
-     * containing sources: [DYNAMIC_IN_RIGID_ID], [STATIC_IN_RIGID_ID], [ISSUE_ONLY_IN_RIGID_ID].
+     * containing sources: [DYNAMIC_IN_STATELESS_ID], [STATIC_IN_STATELESS_ID],
+     * [ISSUE_ONLY_IN_STATELESS_ID].
      */
-    const val MIXED_RIGID_GROUP_ID = "mixed_rigid"
+    const val MIXED_STATELESS_GROUP_ID = "mixed_stateless"
 
     /**
      * ID of a source provided by [COMPLEX_CONFIG], [COMPLEX_ALL_PROFILE_CONFIG], and
@@ -233,35 +275,39 @@
      * ID of a source provided by [COMPLEX_CONFIG], this is a generic, dynamic, primary profile
      * only, visible source.
      */
-    const val DYNAMIC_IN_COLLAPSIBLE_ID = "dynamic_in_collapsible"
+    const val DYNAMIC_IN_STATEFUL_ID = "dynamic_in_stateful"
 
     /**
      * ID of a source provided by [COMPLEX_CONFIG] and [COMPLEX_ALL_PROFILE_CONFIG], this is a
      * generic, dynamic, visible source.
      */
-    const val DYNAMIC_IN_RIGID_ID = "dynamic_in_rigid"
+    const val DYNAMIC_IN_STATELESS_ID = "dynamic_in_stateless"
 
     /**
      * ID of a source provided by [COMPLEX_CONFIG] and [COMPLEX_ALL_PROFILE_CONFIG], this is an
      * issue-only source.
      */
-    const val ISSUE_ONLY_IN_RIGID_ID = "issue_only_in_rigid"
+    const val ISSUE_ONLY_IN_STATELESS_ID = "issue_only_in_stateless"
 
     /**
      * ID of a source provided by [COMPLEX_CONFIG] and [SUMMARY_TEST_CONFIG], this is a generic,
      * static, primary profile only source.
      */
-    const val STATIC_IN_COLLAPSIBLE_ID = "static_in_collapsible"
+    const val STATIC_IN_STATEFUL_ID = "static_in_stateful"
 
     /**
      * ID of a source provided by [COMPLEX_CONFIG] and [COMPLEX_ALL_PROFILE_CONFIG], this is a
      * generic, static source.
      */
-    const val STATIC_IN_RIGID_ID = "static_in_rigid"
+    const val STATIC_IN_STATELESS_ID = "static_in_stateless"
 
     /** Package name for the [DYNAMIC_OTHER_PACKAGE_ID] source. */
     const val OTHER_PACKAGE_NAME = "other_package_name"
 
+    private const val DEDUPLICATION_GROUP_1 = "deduplication_group_1"
+    private const val DEDUPLICATION_GROUP_2 = "deduplication_group_2"
+    private const val DEDUPLICATION_GROUP_3 = "deduplication_group_3"
+
     /** A Simple [SafetyCenterConfig] with an issue only source. */
     val ISSUE_ONLY_SOURCE_CONFIG =
         singleSourceConfig(issueOnlySafetySourceBuilder(ISSUE_ONLY_ALL_OPTIONAL_ID).build())
@@ -271,6 +317,20 @@
         singleSourceConfig(
             issueOnlyAllProfileSafetySourceBuilder(ISSUE_ONLY_ALL_PROFILE_SOURCE_ID).build())
 
+    /**
+     * A Simple [SafetyCenterConfig] with an issue only source inside a [SafetySourcesGroup] with
+     * null title.
+     */
+    val ISSUE_ONLY_SOURCE_NO_GROUP_TITLE_CONFIG =
+        SafetyCenterConfig.Builder()
+            .addSafetySourcesGroup(
+                safetySourcesGroupBuilder(SINGLE_SOURCE_GROUP_ID)
+                    .setTitleResId(Resources.ID_NULL)
+                    .addSafetySource(
+                        issueOnlySafetySourceBuilder(ISSUE_ONLY_ALL_OPTIONAL_ID).build())
+                    .build())
+            .build()
+
     /** A dynamic source with [OTHER_PACKAGE_NAME] */
     val DYNAMIC_OTHER_PACKAGE_SAFETY_SOURCE =
         dynamicSafetySourceBuilder(DYNAMIC_OTHER_PACKAGE_ID)
@@ -296,13 +356,56 @@
                     .build())
             .addSafetySourcesGroup(
                 safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_2)
+                    .setTitleResId(android.R.string.copy)
+                    .setSummaryResId(android.R.string.cancel)
                     .addSafetySource(
                         dynamicSafetySourceBuilder(SOURCE_ID_3)
+                            .setTitleResId(android.R.string.copy)
+                            .setSummaryResId(android.R.string.cancel)
                             .setRefreshOnPageOpenAllowed(false)
                             .build())
                     .build())
             .build()
 
+    /**
+     * A simple [SafetyCenterConfig] for CTS tests with multiple sources with deduplication info.
+     */
+    val multipleSourcesWithDeduplicationInfoConfig: SafetyCenterConfig
+        @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        get() =
+            SafetyCenterConfig.Builder()
+                .addSafetySourcesGroup(
+                    safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_1)
+                        .addSafetySource(
+                            issueOnlySafetySourceWithDuplicationInfo(
+                                SOURCE_ID_1, DEDUPLICATION_GROUP_1))
+                        .addSafetySource(
+                            issueOnlySafetySourceWithDuplicationInfo(
+                                SOURCE_ID_2, DEDUPLICATION_GROUP_1))
+                        .addSafetySource(
+                            issueOnlySafetySourceWithDuplicationInfo(
+                                SOURCE_ID_3, DEDUPLICATION_GROUP_2))
+                        .addSafetySource(
+                            issueOnlySafetySourceWithDuplicationInfo(
+                                SOURCE_ID_4, DEDUPLICATION_GROUP_3))
+                        .build())
+                .addSafetySourcesGroup(
+                    safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_2)
+                        .addSafetySource(
+                            issueOnlySafetySourceWithDuplicationInfo(
+                                SOURCE_ID_5, DEDUPLICATION_GROUP_1))
+                        .addSafetySource(
+                            issueOnlySafetySourceWithDuplicationInfo(
+                                SOURCE_ID_6, DEDUPLICATION_GROUP_3))
+                        .build())
+                .addSafetySourcesGroup(
+                    safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_3)
+                        .addSafetySource(
+                            issueOnlySafetySourceWithDuplicationInfo(
+                                SOURCE_ID_7, DEDUPLICATION_GROUP_3))
+                        .build())
+                .build()
+
     /** Source included in [DYNAMIC_SOURCE_GROUP_1]. */
     val DYNAMIC_SOURCE_1 = dynamicSafetySource(SOURCE_ID_1)
 
@@ -448,7 +551,7 @@
         SafetyCenterConfig.Builder()
             .addSafetySourcesGroup(
                 safetySourcesGroupBuilder(ANDROID_LOCK_SCREEN_SOURCES_GROUP_ID)
-                    // This is needed to have a collapsible group with an empty summary
+                    // This is needed to have a stateful group with an empty summary
                     .setSummaryResId(Resources.ID_NULL)
                     .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
                     .addSafetySource(
@@ -482,7 +585,7 @@
                     .addSafetySource(dynamicSafetySource(SOURCE_ID_5))
                     .addSafetySource(dynamicSafetySource(SOURCE_ID_6))
                     .addSafetySource(dynamicSafetySource(SOURCE_ID_7))
-                    .addSafetySource(staticSafetySource(STATIC_IN_COLLAPSIBLE_ID))
+                    .addSafetySource(staticSafetySource(STATIC_IN_STATEFUL_ID))
                     .build())
             .build()
 
@@ -504,6 +607,16 @@
                             .setMaxSeverityLevel(SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION)
                             .setSearchTermsResId(android.R.string.ok)
                             .setLoggingAllowed(false)
+                            .apply {
+                                if (SdkLevel.isAtLeastU()) {
+                                    setNotificationsAllowed(true)
+                                    setDeduplicationGroup("group")
+                                    addPackageCertificateHash(PACKAGE_CERT_HASH_FAKE)
+                                    PACKAGE_CERT_HASHES_CTS.forEach {
+                                        addPackageCertificateHash(it)
+                                    }
+                                }
+                            }
                             .build())
                     .addSafetySource(
                         dynamicSafetySourceBuilder(DYNAMIC_DISABLED_ID)
@@ -528,7 +641,14 @@
                     .build())
             .addSafetySourcesGroup(
                 safetySourcesGroupBuilder(STATIC_GROUP_ID)
-                    .setSummaryResId(Resources.ID_NULL)
+                    .apply {
+                        if (SdkLevel.isAtLeastU()) {
+                            setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS)
+                            setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+                        } else {
+                            setSummaryResId(Resources.ID_NULL)
+                        }
+                    }
                     .addSafetySource(
                         staticSafetySourceBuilder(STATIC_BAREBONE_ID)
                             .setSummaryResId(Resources.ID_NULL)
@@ -536,11 +656,20 @@
                     .addSafetySource(
                         staticSafetySourceBuilder(STATIC_ALL_OPTIONAL_ID)
                             .setSearchTermsResId(android.R.string.ok)
+                            .apply { if (SdkLevel.isAtLeastU()) setPackageName(CTS_PACKAGE_NAME) }
                             .build())
                     .build())
             .addSafetySourcesGroup(
                 SafetySourcesGroup.Builder()
                     .setId(ISSUE_ONLY_GROUP_ID)
+                    .apply {
+                        if (SdkLevel.isAtLeastU()) {
+                            setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN)
+                            setTitleResId(android.R.string.ok)
+                            setSummaryResId(android.R.string.ok)
+                            setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY)
+                        }
+                    }
                     .addSafetySource(
                         issueOnlySafetySourceBuilder(ISSUE_ONLY_BAREBONE_ID)
                             .setRefreshOnPageOpenAllowed(false)
@@ -549,19 +678,29 @@
                         issueOnlySafetySourceBuilder(ISSUE_ONLY_ALL_OPTIONAL_ID)
                             .setMaxSeverityLevel(SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION)
                             .setLoggingAllowed(false)
+                            .apply {
+                                if (SdkLevel.isAtLeastU()) {
+                                    setNotificationsAllowed(true)
+                                    setDeduplicationGroup("group")
+                                    addPackageCertificateHash(PACKAGE_CERT_HASH_FAKE)
+                                    PACKAGE_CERT_HASHES_CTS.forEach {
+                                        addPackageCertificateHash(it)
+                                    }
+                                }
+                            }
                             .build())
                     .build())
             .addSafetySourcesGroup(
-                safetySourcesGroupBuilder(MIXED_COLLAPSIBLE_GROUP_ID)
-                    .addSafetySource(dynamicSafetySource(DYNAMIC_IN_COLLAPSIBLE_ID))
-                    .addSafetySource(staticSafetySource(STATIC_IN_COLLAPSIBLE_ID))
+                safetySourcesGroupBuilder(MIXED_STATEFUL_GROUP_ID)
+                    .addSafetySource(dynamicSafetySource(DYNAMIC_IN_STATEFUL_ID))
+                    .addSafetySource(staticSafetySource(STATIC_IN_STATEFUL_ID))
                     .build())
             .addSafetySourcesGroup(
-                safetySourcesGroupBuilder(MIXED_RIGID_GROUP_ID)
+                safetySourcesGroupBuilder(MIXED_STATELESS_GROUP_ID)
                     .setSummaryResId(Resources.ID_NULL)
-                    .addSafetySource(dynamicSafetySource(DYNAMIC_IN_RIGID_ID))
-                    .addSafetySource(staticSafetySource(STATIC_IN_RIGID_ID))
-                    .addSafetySource(issueOnlySafetySource(ISSUE_ONLY_IN_RIGID_ID))
+                    .addSafetySource(dynamicSafetySource(DYNAMIC_IN_STATELESS_ID))
+                    .addSafetySource(staticSafetySource(STATIC_IN_STATELESS_ID))
+                    .addSafetySource(issueOnlySafetySource(ISSUE_ONLY_IN_STATELESS_ID))
                     .build())
             .build()
 
@@ -601,6 +740,7 @@
                     .addSafetySource(
                         staticAllProfileSafetySourceBuilder(STATIC_ALL_OPTIONAL_ID)
                             .setSearchTermsResId(android.R.string.ok)
+                            .apply { if (SdkLevel.isAtLeastU()) setPackageName(CTS_PACKAGE_NAME) }
                             .build())
                     .build())
             .addSafetySourcesGroup(
@@ -614,23 +754,55 @@
                         issueOnlyAllProfileSafetySourceBuilder(ISSUE_ONLY_ALL_OPTIONAL_ID)
                             .setMaxSeverityLevel(SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION)
                             .setLoggingAllowed(false)
+                            .apply {
+                                if (SdkLevel.isAtLeastU()) {
+                                    setNotificationsAllowed(true)
+                                    setDeduplicationGroup("group")
+                                    addPackageCertificateHash(PACKAGE_CERT_HASH_FAKE)
+                                    PACKAGE_CERT_HASHES_CTS.forEach {
+                                        addPackageCertificateHash(it)
+                                    }
+                                }
+                            }
                             .build())
                     .build())
             .addSafetySourcesGroup(
-                safetySourcesGroupBuilder(MIXED_RIGID_GROUP_ID)
+                safetySourcesGroupBuilder(MIXED_STATELESS_GROUP_ID)
                     .setSummaryResId(Resources.ID_NULL)
                     .addSafetySource(
-                        dynamicAllProfileSafetySourceBuilder(DYNAMIC_IN_RIGID_ID).build())
+                        dynamicAllProfileSafetySourceBuilder(DYNAMIC_IN_STATELESS_ID).build())
                     .addSafetySource(
-                        staticAllProfileSafetySourceBuilder(STATIC_IN_RIGID_ID).build())
+                        staticAllProfileSafetySourceBuilder(STATIC_IN_STATELESS_ID).build())
                     .addSafetySource(
-                        issueOnlyAllProfileSafetySourceBuilder(ISSUE_ONLY_IN_RIGID_ID).build())
+                        issueOnlyAllProfileSafetySourceBuilder(ISSUE_ONLY_IN_STATELESS_ID).build())
+                    .build())
+            .build()
+
+    /**
+     * A [SafetyCenterConfig] containing only hidden sources.
+     */
+    val HIDDEN_ONLY_CONFIG =
+        SafetyCenterConfig.Builder()
+            .addSafetySourcesGroup(
+                safetySourcesGroupBuilder("some_collapsible_group")
+                    .addSafetySource(
+                        dynamicSafetySourceBuilder("some_hidden_source")
+                            .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN)
+                            .build())
+                    .build())
+            .addSafetySourcesGroup(
+                safetySourcesGroupBuilder("some_rigid_group")
+                    .setSummaryResId(Resources.ID_NULL)
+                    .addSafetySource(
+                        dynamicSafetySourceBuilder("another_hidden_source")
+                            .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN)
+                            .build())
                     .build())
             .build()
 
     private fun dynamicSafetySource(id: String) = dynamicSafetySourceBuilder(id).build()
 
-    private fun dynamicSafetySourceBuilder(id: String) =
+    fun dynamicSafetySourceBuilder(id: String) =
         SafetySource.Builder(SAFETY_SOURCE_TYPE_DYNAMIC)
             .setId(id)
             .setPackageName(CTS_PACKAGE_NAME)
@@ -660,6 +832,10 @@
             .setProfile(SafetySource.PROFILE_ALL)
             .setTitleForWorkResId(android.R.string.paste)
 
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    private fun issueOnlySafetySourceWithDuplicationInfo(id: String, deduplicationGroup: String) =
+        issueOnlySafetySourceBuilder(id).setDeduplicationGroup(deduplicationGroup).build()
+
     private fun issueOnlySafetySource(id: String) = issueOnlySafetySourceBuilder(id).build()
 
     private fun issueOnlySafetySourceBuilder(id: String) =
@@ -678,7 +854,7 @@
             .setTitleResId(android.R.string.ok)
             .setSummaryResId(android.R.string.ok)
 
-    private fun singleSourceConfig(safetySource: SafetySource) =
+    fun singleSourceConfig(safetySource: SafetySource) =
         SafetyCenterConfig.Builder()
             .addSafetySourcesGroup(
                 safetySourcesGroupBuilder(SINGLE_SOURCE_GROUP_ID)
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsData.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsData.kt
index 3602118..640e5bb 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsData.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsData.kt
@@ -19,6 +19,8 @@
 import android.app.PendingIntent
 import android.content.Context
 import android.icu.text.MessageFormat
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+import android.os.Bundle
 import android.os.UserHandle
 import android.safetycenter.SafetyCenterData
 import android.safetycenter.SafetyCenterEntry
@@ -44,7 +46,8 @@
 import android.safetycenter.cts.testing.SafetySourceCtsData.Companion.RECOMMENDATION_ISSUE_ACTION_ID
 import android.safetycenter.cts.testing.SafetySourceCtsData.Companion.RECOMMENDATION_ISSUE_ID
 import android.util.ArrayMap
-import com.android.safetycenter.internaldata.SafetyCenterEntryGroupId
+import androidx.annotation.RequiresApi
+import com.android.modules.utils.build.SdkLevel
 import com.android.safetycenter.internaldata.SafetyCenterEntryId
 import com.android.safetycenter.internaldata.SafetyCenterIds
 import com.android.safetycenter.internaldata.SafetyCenterIssueActionId
@@ -75,6 +78,19 @@
             .build()
 
     /**
+     * Returns a [SafetyCenterStatus] with one alert and the given [statusResource] and
+     * [overallSeverityLevel].
+     */
+    fun safetyCenterStatusOneAlert(
+        statusResource: String,
+        overallSeverityLevel: Int
+    ): SafetyCenterStatus =
+        SafetyCenterStatus.Builder(
+                safetyCenterResourcesContext.getStringByName(statusResource), getAlertString(1))
+            .setSeverityLevel(overallSeverityLevel)
+            .build()
+
+    /**
      * Returns the [SafetyCenterStatus] used when the overall status is critical and no scan is in
      * progress for the given number of alerts.
      */
@@ -213,7 +229,11 @@
      * Returns an information [SafetyCenterIssue] for the given source and user id that is
      * consistent with information [SafetySourceIssue]s used in [SafetySourceCtsData].
      */
-    fun safetyCenterIssueInformation(sourceId: String, userId: Int = UserHandle.myUserId()) =
+    fun safetyCenterIssueInformation(
+        sourceId: String,
+        userId: Int = UserHandle.myUserId(),
+        attributionTitle: String? = "OK"
+    ) =
         SafetyCenterIssue.Builder(
                 issueId(sourceId, INFORMATION_ISSUE_ID, userId = userId),
                 "Information issue title",
@@ -231,13 +251,18 @@
                             "Review",
                             safetySourceCtsData.testActivityRedirectPendingIntent)
                         .build()))
+            .apply { if (SdkLevel.isAtLeastU()) setAttributionTitle(attributionTitle) }
             .build()
 
     /**
      * Returns a recommendation [SafetyCenterIssue] for the given source and user id that is
      * consistent with recommendation [SafetySourceIssue]s used in [SafetySourceCtsData].
      */
-    fun safetyCenterIssueRecommendation(sourceId: String, userId: Int = UserHandle.myUserId()) =
+    fun safetyCenterIssueRecommendation(
+        sourceId: String,
+        userId: Int = UserHandle.myUserId(),
+        attributionTitle: String? = "OK"
+    ) =
         SafetyCenterIssue.Builder(
                 issueId(sourceId, RECOMMENDATION_ISSUE_ID, userId = userId),
                 "Recommendation issue title",
@@ -254,6 +279,7 @@
                             "See issue",
                             safetySourceCtsData.testActivityRedirectPendingIntent)
                         .build()))
+            .apply { if (SdkLevel.isAtLeastU()) setAttributionTitle(attributionTitle) }
             .build()
 
     /**
@@ -263,7 +289,8 @@
     fun safetyCenterIssueCritical(
         sourceId: String,
         isActionInFlight: Boolean = false,
-        userId: Int = UserHandle.myUserId()
+        userId: Int = UserHandle.myUserId(),
+        attributionTitle: String? = "OK"
     ) =
         SafetyCenterIssue.Builder(
                 issueId(sourceId, CRITICAL_ISSUE_ID, userId = userId),
@@ -280,6 +307,7 @@
                         .setWillResolve(true)
                         .setIsInFlight(isActionInFlight)
                         .build()))
+            .apply { if (SdkLevel.isAtLeastU()) setAttributionTitle(attributionTitle) }
             .build()
 
     /**
@@ -313,13 +341,6 @@
                 emptyList(),
                 emptyList())
 
-        /** Creates an ID for a Safety Center entry group. */
-        fun entryGroupId(sourcesGroupId: String) =
-            SafetyCenterIds.encodeToString(
-                SafetyCenterEntryGroupId.newBuilder()
-                    .setSafetySourcesGroupId(sourcesGroupId)
-                    .build())
-
         /** Creates an ID for a Safety Center entry. */
         fun entryId(sourceId: String, userId: Int = UserHandle.myUserId()) =
             SafetyCenterIds.encodeToString(
@@ -363,5 +384,57 @@
                             .build())
                     .setSafetySourceIssueActionId(sourceIssueActionId)
                     .build())
+
+        /**
+         * On U+, returns a new [SafetyCenterData] with the dismissed issues set. Prior to U,
+         * returns the passed in [SafetyCenterData].
+         */
+        fun SafetyCenterData.withDismissedIssuesIfAtLeastU(
+            dismissedIssues: List<SafetyCenterIssue>
+        ): SafetyCenterData =
+            if (SdkLevel.isAtLeastU()) {
+                copy(dismissedIssues = dismissedIssues)
+            } else this
+
+        /**
+         * On U+, returns a new [SafetyCenterData] with [SafetyCenterIssue]s having the
+         * [attributionTitle]. Prior to U, returns the passed in [SafetyCenterData].
+         */
+        fun SafetyCenterData.withAttributionTitleInIssuesIfAtLeastU(
+            attributionTitle: String?
+        ): SafetyCenterData {
+            return if (SdkLevel.isAtLeastU()) {
+                val issuesWithAttributionTitle =
+                    this.issues.map {
+                        SafetyCenterIssue.Builder(it).setAttributionTitle(attributionTitle).build()
+                    }
+                copy(issues = issuesWithAttributionTitle)
+            } else this
+        }
+
+        /**
+         * On U+, returns a new [SafetyCenterData] with the extras set. Prior to U, returns the
+         * passed in [SafetyCenterData].
+         */
+        fun SafetyCenterData.withExtrasIfAtLeastU(extras: Bundle): SafetyCenterData =
+            if (SdkLevel.isAtLeastU()) {
+                copy(extras = extras)
+            } else this
+
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        private fun SafetyCenterData.copy(
+            issues: List<SafetyCenterIssue> = this.issues,
+            dismissedIssues: List<SafetyCenterIssue> = this.dismissedIssues,
+            extras: Bundle = this.extras
+        ): SafetyCenterData =
+            SafetyCenterData.Builder(status)
+                .apply {
+                    issues.forEach(::addIssue)
+                    entriesOrGroups.forEach(::addEntryOrGroup)
+                    staticEntryGroups.forEach(::addStaticEntryGroup)
+                    dismissedIssues.forEach(::addDismissedIssue)
+                }
+                .setExtras(extras)
+                .build()
     }
 }
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterFlags.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterFlags.kt
index f91cd33..ad7e2d0 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterFlags.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterFlags.kt
@@ -18,9 +18,12 @@
 
 import android.Manifest.permission.READ_DEVICE_CONFIG
 import android.Manifest.permission.WRITE_DEVICE_CONFIG
+import android.annotation.TargetApi
+import android.app.job.JobInfo
 import android.content.Context
 import android.content.pm.PackageManager
 import android.content.res.Resources
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
 import android.provider.DeviceConfig
 import android.provider.DeviceConfig.NAMESPACE_PRIVACY
 import android.provider.DeviceConfig.Properties
@@ -28,6 +31,7 @@
 import android.safetycenter.SafetyCenterManager.REFRESH_REASON_DEVICE_REBOOT
 import android.safetycenter.SafetyCenterManager.REFRESH_REASON_OTHER
 import android.safetycenter.SafetyCenterManager.REFRESH_REASON_PAGE_OPEN
+import android.safetycenter.SafetyCenterManager.REFRESH_REASON_PERIODIC
 import android.safetycenter.SafetyCenterManager.REFRESH_REASON_RESCAN_BUTTON_CLICK
 import android.safetycenter.SafetyCenterManager.REFRESH_REASON_SAFETY_CENTER_ENABLED
 import android.safetycenter.SafetySourceData
@@ -43,6 +47,20 @@
     private val isEnabledFlag =
         Flag("safety_center_is_enabled", defaultValue = false, BooleanParser())
 
+    /** Flag that determines whether Safety Center can send notifications. */
+    private val notificationsFlag =
+        Flag("safety_center_notifications_enabled", defaultValue = false, BooleanParser())
+
+    /**
+     * Flag containing a comma delimited list of IDs of sources that Safety Center can send
+     * notifications about, in addition to those permitted by the current XML config.
+     */
+    private val notificationsAllowedSourcesFlag =
+        Flag(
+            "safety_center_notifications_allowed_sources",
+            defaultValue = emptySet(),
+            SetParser(StringParser()))
+
     /**
      * Flag that determines whether we should show error entries for sources that timeout when
      * refreshing them.
@@ -161,10 +179,57 @@
             defaultValue = PackageManager.DONT_KILL_APP,
             IntParser())
 
+    /**
+     * Flag that determines whether to show subpages in the Safety Center UI instead of the
+     * expand-and-collapse list.
+     */
+    private val showSubpagesFlag =
+        Flag("safety_center_show_subpages", defaultValue = false, BooleanParser())
+
+    private val overrideRefreshOnPageOpenSourcesFlag =
+        Flag(
+            "safety_center_override_refresh_on_page_open_sources",
+            defaultValue = setOf(),
+            SetParser(StringParser()))
+
+    /**
+     * Flag that enables both one-off and periodic background refreshes in
+     * [SafetyCenterBackgroundRefreshJobService].
+     */
+    private val backgroundRefreshIsEnabledFlag =
+        Flag(
+            "safety_center_background_refresh_is_enabled",
+            // do not set defaultValue to true, do not want background refreshes running
+            // during other tests
+            defaultValue = false,
+            BooleanParser())
+
+    /**
+     * Flag that determines how often periodic background refreshes are run in
+     * [SafetyCenterBackgroundRefreshJobService]. See [JobInfo.setPeriodic] for details.
+     *
+     * Note that jobs may take longer than this to be scheduled, or may possibly never run,
+     * depending on whether the other constraints on the job get satisfied.
+     */
+    private val periodicBackgroundRefreshIntervalFlag =
+        Flag(
+            "safety_center_periodic_background_interval_millis",
+            defaultValue = Duration.ofDays(1),
+            DurationParser())
+
+    /**
+     * Flag that determines whether background refreshes require charging in
+     * [SafetyCenterBackgroundRefreshJobService]. See [JobInfo.setRequiresCharging] for details.
+     */
+    private val backgroundRefreshRequiresChargingFlag =
+        Flag("safety_center_background_requires_charging", defaultValue = false, BooleanParser())
+
     /** Every Safety Center flag. */
     private val FLAGS: List<Flag<*>> =
         listOf(
             isEnabledFlag,
+            notificationsFlag,
+            notificationsAllowedSourcesFlag,
             showErrorEntriesOnTimeoutFlag,
             replaceLockScreenIconActionFlag,
             refreshSourceTimeoutsFlag,
@@ -176,7 +241,12 @@
             issueCategoryAllowlistsFlag,
             backgroundRefreshDeniedSourcesFlag,
             allowStatsdLoggingInTestsFlag,
-            qsTileComponentSettingFlag)
+            qsTileComponentSettingFlag,
+            showSubpagesFlag,
+            overrideRefreshOnPageOpenSourcesFlag,
+            backgroundRefreshIsEnabledFlag,
+            periodicBackgroundRefreshIntervalFlag,
+            backgroundRefreshRequiresChargingFlag)
 
     /** Returns whether the device supports Safety Center. */
     fun Context.deviceSupportsSafetyCenter() =
@@ -186,6 +256,12 @@
     /** A property that allows getting and setting the [isEnabledFlag]. */
     var isEnabled: Boolean by isEnabledFlag
 
+    /** A property that allows getting and setting the [notificationsFlag]. */
+    var notificationsEnabled: Boolean by notificationsFlag
+
+    /** A property that allowed getting and setting the [notificationsAllowedSourcesFlag]. */
+    var notificationsAllowedSources: Set<String> by notificationsAllowedSourcesFlag
+
     /** A property that allows getting and setting the [showErrorEntriesOnTimeoutFlag]. */
     var showErrorEntriesOnTimeout: Boolean by showErrorEntriesOnTimeoutFlag
 
@@ -219,6 +295,21 @@
     /** A property that allows getting and setting the [allowStatsdLoggingInTestsFlag]. */
     var allowStatsdLoggingInTests: Boolean by allowStatsdLoggingInTestsFlag
 
+    /** A property that allows getting and setting the [showSubpagesFlag]. */
+    var showSubpages: Boolean by showSubpagesFlag
+
+    /** A property that allows getting and setting the [overrideRefreshOnPageOpenSourcesFlag]. */
+    var overrideRefreshOnPageOpenSources: Set<String> by overrideRefreshOnPageOpenSourcesFlag
+
+    /** A property that allows getting and settings the [backgroundRefreshIsEnabledFlag]. */
+    var backgroundRefreshIsEnabled: Boolean by backgroundRefreshIsEnabledFlag
+
+    /** A property that allows getting and settings the [periodicBackgroundRefreshIntervalFlag]. */
+    var periodicBackgroundRefreshInterval: Duration by periodicBackgroundRefreshIntervalFlag
+
+    /** A property that allows getting and settings the [backgroundRefreshRequiresChargingFlag]. */
+    var backgroundRefreshRequiresCharging: Boolean by backgroundRefreshRequiresChargingFlag
+
     /**
      * Returns a snapshot of all the Safety Center flags.
      *
@@ -274,14 +365,16 @@
     fun Properties.isSafetyCenterEnabled() =
         getBoolean(isEnabledFlag.name, /* defaultValue */ false)
 
-    private fun getAllRefreshTimeoutsMap(refreshTimeout: Duration) =
+    @TargetApi(UPSIDE_DOWN_CAKE)
+    private fun getAllRefreshTimeoutsMap(refreshTimeout: Duration): Map<Int, Duration> =
         mapOf(
             REFRESH_REASON_PAGE_OPEN to refreshTimeout,
             REFRESH_REASON_RESCAN_BUTTON_CLICK to refreshTimeout,
             REFRESH_REASON_DEVICE_REBOOT to refreshTimeout,
             REFRESH_REASON_DEVICE_LOCALE_CHANGE to refreshTimeout,
             REFRESH_REASON_SAFETY_CENTER_ENABLED to refreshTimeout,
-            REFRESH_REASON_OTHER to refreshTimeout)
+            REFRESH_REASON_OTHER to refreshTimeout,
+            REFRESH_REASON_PERIODIC to refreshTimeout)
 
     private interface Parser<T> {
         fun parseFromString(stringValue: String): T
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceCtsData.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceCtsData.kt
index ce3d340..c0e189f 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceCtsData.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceCtsData.kt
@@ -20,6 +20,8 @@
 import android.content.Context
 import android.content.Intent
 import android.content.Intent.FLAG_RECEIVER_FOREGROUND
+import android.content.pm.PackageManager.ResolveInfoFlags
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
 import android.safetycenter.SafetyEvent
 import android.safetycenter.SafetySourceData
 import android.safetycenter.SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING
@@ -31,6 +33,7 @@
 import android.safetycenter.SafetySourceStatus
 import android.safetycenter.SafetySourceStatus.IconAction
 import android.safetycenter.SafetySourceStatus.IconAction.ICON_TYPE_GEAR
+import android.safetycenter.SafetySourceStatus.IconAction.ICON_TYPE_INFO
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ACTION_TEST_ACTIVITY
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_ID
 import android.safetycenter.cts.testing.SafetySourceIntentHandler.Companion.ACTION_DISMISS_ISSUE
@@ -38,6 +41,8 @@
 import android.safetycenter.cts.testing.SafetySourceIntentHandler.Companion.EXTRA_SOURCE_ID
 import android.safetycenter.cts.testing.SafetySourceIntentHandler.Companion.EXTRA_SOURCE_ISSUE_ACTION_ID
 import android.safetycenter.cts.testing.SafetySourceIntentHandler.Companion.EXTRA_SOURCE_ISSUE_ID
+import androidx.annotation.RequiresApi
+import java.lang.IllegalStateException
 import kotlin.math.max
 
 /**
@@ -77,7 +82,12 @@
             .build()
 
     /** A [SafetySourceIssue] with a [SEVERITY_LEVEL_INFORMATION] and a redirecting [Action]. */
-    val informationIssue =
+    val informationIssue = defaultInformationIssueBuilder().build()
+
+    /**
+     * A [SafetySourceIssue.Builder] with a [SEVERITY_LEVEL_INFORMATION] and a redirecting [Action].
+     */
+    private fun defaultInformationIssueBuilder() =
         SafetySourceIssue.Builder(
                 INFORMATION_ISSUE_ID,
                 "Information issue title",
@@ -88,7 +98,6 @@
                 Action.Builder(
                         INFORMATION_ISSUE_ACTION_ID, "Review", testActivityRedirectPendingIntent)
                     .build())
-            .build()
 
     /**
      * A [SafetySourceIssue] with a [SEVERITY_LEVEL_INFORMATION] and a redirecting [Action]. With
@@ -168,6 +177,19 @@
             .setStatus(
                 SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION)
                     .setPendingIntent(testActivityRedirectPendingIntent)
+                    .setIconAction(IconAction(ICON_TYPE_INFO, testActivityRedirectPendingIntent))
+                    .build())
+            .build()
+
+    /**
+     * A [SafetySourceData] with a [SEVERITY_LEVEL_INFORMATION] [SafetySourceStatus] and an
+     * [IconAction] having a [ICON_TYPE_GEAR].
+     */
+    val informationWithGearIconAction =
+        SafetySourceData.Builder()
+            .setStatus(
+                SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION)
+                    .setPendingIntent(testActivityRedirectPendingIntent)
                     .setIconAction(IconAction(ICON_TYPE_GEAR, testActivityRedirectPendingIntent))
                     .build())
             .build()
@@ -186,6 +208,24 @@
             .build()
 
     /**
+     * A [SafetySourceData] with a [SEVERITY_LEVEL_INFORMATION] redirecting a [SafetySourceIssue]
+     * having a [SafetySourceIssue.mAttributionTitle] and [SafetySourceStatus].
+     */
+    val informationWithIssueWithAttributionTitle: SafetySourceData
+        @RequiresApi(UPSIDE_DOWN_CAKE)
+        get() =
+            SafetySourceData.Builder()
+                .setStatus(
+                    SafetySourceStatus.Builder("Ok title", "Ok summary", SEVERITY_LEVEL_INFORMATION)
+                        .setPendingIntent(testActivityRedirectPendingIntent)
+                        .build())
+                .addIssue(
+                    defaultInformationIssueBuilder()
+                        .setAttributionTitle("Attribution Title")
+                        .build())
+                .build()
+
+    /**
      * A [SafetySourceData] with a [SEVERITY_LEVEL_INFORMATION] redirecting [SafetySourceIssue] and
      * [SafetySourceStatus], to be used for a managed profile entry.
      */
@@ -216,11 +256,14 @@
      * A [SafetySourceIssue.Builder] with a [SEVERITY_LEVEL_RECOMMENDATION] and a redirecting
      * [Action].
      */
-    private fun defaultRecommendationIssueBuilder() =
+    fun defaultRecommendationIssueBuilder(
+        title: String = "Recommendation issue title",
+        summary: String = "Recommendation issue summary"
+    ) =
         SafetySourceIssue.Builder(
                 RECOMMENDATION_ISSUE_ID,
-                "Recommendation issue title",
-                "Recommendation issue summary",
+                title,
+                summary,
                 SEVERITY_LEVEL_RECOMMENDATION,
                 ISSUE_TYPE_ID)
             .addAction(
@@ -237,6 +280,14 @@
     val recommendationGeneralIssue = defaultRecommendationIssueBuilder().build()
 
     /**
+     * A [SafetySourceIssue] with a [SEVERITY_LEVEL_RECOMMENDATION], general category, redirecting
+     * [Action] and with deduplication id.
+     */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    fun recommendationIssueWithDeduplicationId(deduplicationId: String) =
+        defaultRecommendationIssueBuilder().setDeduplicationId(deduplicationId).build()
+
+    /**
      * A [SafetySourceIssue] with a [SEVERITY_LEVEL_RECOMMENDATION], account category and a
      * redirecting [Action].
      */
@@ -393,6 +444,14 @@
     val criticalResolvingGeneralIssue = defaultCriticalResolvingIssueBuilder().build()
 
     /**
+     * General [SafetySourceIssue] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and with deduplication
+     * info and a resolving [Action].
+     */
+    @RequiresApi(UPSIDE_DOWN_CAKE)
+    fun criticalIssueWithDeduplicationId(deduplicationId: String) =
+        defaultCriticalResolvingIssueBuilder().setDeduplicationId(deduplicationId).build()
+
+    /**
      * Account related [SafetySourceIssue] with a [SEVERITY_LEVEL_CRITICAL_WARNING] and a resolving
      * [Action].
      */
@@ -566,8 +625,23 @@
         }
 
         /** Returns a [PendingIntent] that redirects to [intent]. */
-        fun createRedirectPendingIntent(context: Context, intent: Intent): PendingIntent =
-            PendingIntent.getActivity(
-                context, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE)
+        fun createRedirectPendingIntent(context: Context, intent: Intent): PendingIntent {
+            val explicitIntent = Intent(intent).setPackage(context.packageName)
+            val redirectIntent =
+                if (intentResolves(context, intent)) {
+                    intent
+                } else if (intentResolves(context, explicitIntent)) {
+                    explicitIntent
+                } else {
+                    throw IllegalStateException("Intent doesn't resolve")
+                }
+            return PendingIntent.getActivity(
+                context, 0 /* requestCode */, redirectIntent, PendingIntent.FLAG_IMMUTABLE)
+        }
+
+        private fun intentResolves(context: Context, intent: Intent): Boolean =
+            context.packageManager
+                .queryIntentActivities(intent, ResolveInfoFlags.of(0))
+                .isNotEmpty()
     }
 }
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceReceiver.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceReceiver.kt
index 12b68fd..c377a2b 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceReceiver.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetySourceReceiver.kt
@@ -139,17 +139,20 @@
 
         fun SafetyCenterManager.refreshSafetySourcesWithReceiverPermissionAndWait(
             refreshReason: Int,
-            timeout: Duration = TIMEOUT_LONG
+            timeout: Duration = TIMEOUT_LONG,
+            safetySourceIds: List<String>? = null
         ) =
             callWithShellPermissionIdentity(SEND_SAFETY_CENTER_UPDATE) {
-                refreshSafetySourcesWithoutReceiverPermissionAndWait(refreshReason, timeout)
+                refreshSafetySourcesWithoutReceiverPermissionAndWait(
+                    refreshReason, timeout, safetySourceIds)
             }
 
         fun SafetyCenterManager.refreshSafetySourcesWithoutReceiverPermissionAndWait(
             refreshReason: Int,
-            timeout: Duration
+            timeout: Duration,
+            safetySourceIds: List<String>? = null
         ): String {
-            refreshSafetySourcesWithPermission(refreshReason)
+            refreshSafetySourcesWithPermission(refreshReason, safetySourceIds)
             if (timeout < TIMEOUT_LONG) {
                 getApplicationContext().waitForBroadcastIdle()
             }
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/UiTestHelper.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/UiTestHelper.kt
index f668f0a..99fb7f2 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/UiTestHelper.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/UiTestHelper.kt
@@ -24,6 +24,7 @@
 import android.support.test.uiautomator.StaleObjectException
 import android.support.test.uiautomator.UiObject2
 import android.util.Log
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
 import com.android.compatibility.common.util.UiAutomatorUtils.getUiDevice
 import com.android.compatibility.common.util.UiAutomatorUtils.waitFindObject
 import com.android.compatibility.common.util.UiAutomatorUtils.waitFindObjectOrNull
@@ -117,6 +118,19 @@
         waitDisplayed(By.text("See all alerts")) { it.click() }
     }
 
+    /** Enables or disables animations based on [enabled]. */
+    fun setAnimationsEnabled(enabled: Boolean) {
+        val scale =
+            if (enabled) {
+                "1"
+            } else {
+                "0"
+            }
+        runShellCommand("settings put global window_animation_scale $scale")
+        runShellCommand("settings put global transition_animation_scale $scale")
+        runShellCommand("settings put global animator_duration_scale $scale")
+    }
+
     private fun buttonSelector(label: CharSequence): BySelector {
         return By.clickable(true).text(Pattern.compile("$label|${label.toString().uppercase()}"))
     }
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterActivityTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterActivityTest.kt
index c51f18d..cfe0134 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterActivityTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterActivityTest.kt
@@ -17,6 +17,9 @@
 package android.safetycenter.cts.ui
 
 import android.content.Context
+import android.os.Build.VERSION.CODENAME
+import android.os.Build.VERSION_CODES.TIRAMISU
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
 import android.os.Bundle
 import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_ID
 import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_ISSUE_ID
@@ -33,6 +36,8 @@
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_SOURCE_GROUP_1
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_SOURCE_GROUP_2
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.DYNAMIC_SOURCE_GROUP_3
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_ALL_OPTIONAL_ID
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.ISSUE_ONLY_SOURCE_NO_GROUP_TITLE_CONFIG
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MULTIPLE_SOURCES_CONFIG
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MULTIPLE_SOURCES_CONFIG_WITH_SOURCE_WITH_INVALID_INTENT
 import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MULTIPLE_SOURCE_GROUPS_CONFIG
@@ -56,6 +61,7 @@
 import android.safetycenter.cts.testing.SettingsPackage.getSettingsPackageName
 import android.safetycenter.cts.testing.UiTestHelper.RESCAN_BUTTON_LABEL
 import android.safetycenter.cts.testing.UiTestHelper.expandMoreIssuesCard
+import android.safetycenter.cts.testing.UiTestHelper.setAnimationsEnabled
 import android.safetycenter.cts.testing.UiTestHelper.waitAllTextDisplayed
 import android.safetycenter.cts.testing.UiTestHelper.waitAllTextNotDisplayed
 import android.safetycenter.cts.testing.UiTestHelper.waitButtonDisplayed
@@ -68,17 +74,27 @@
 import android.support.test.uiautomator.UiDevice
 import androidx.test.core.app.ApplicationProvider.getApplicationContext
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.android.compatibility.common.util.DisableAnimationRule
+import com.android.compatibility.common.util.FreezeRotationRule
 import com.android.compatibility.common.util.UiAutomatorUtils.getUiDevice
 import java.time.Duration
 import org.junit.After
+import org.junit.Assume.assumeFalse
 import org.junit.Assume.assumeTrue
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
 /** CTS tests for the Safety Center Activity. */
 @RunWith(AndroidJUnit4::class)
 class SafetyCenterActivityTest {
+
+    @get:Rule val disableAnimationRule = DisableAnimationRule()
+
+    @get:Rule val freezeRotationRule = FreezeRotationRule()
+
     private val context: Context = getApplicationContext()
 
     private val safetyCenterCtsHelper = SafetyCenterCtsHelper(context)
@@ -445,6 +461,19 @@
     }
 
     @Test
+    fun entryListWithSingleSource_clickingTheIconActionButton_redirectsToDifferentScreen() {
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+        safetyCenterCtsHelper.setData(
+            SINGLE_SOURCE_ID, safetySourceCtsData.informationWithIconAction)
+
+        context.launchSafetyCenterActivity {
+            waitDisplayed(By.desc("Information")) { it.click() }
+            waitButtonDisplayed("Exit test activity") { it.click() }
+            waitDisplayed(By.text("Ok title"))
+        }
+    }
+
+    @Test
     fun staticSource_clickingTheEntry_redirectsToDifferentScreen() {
         safetyCenterCtsHelper.setConfig(STATIC_SOURCES_CONFIG)
 
@@ -605,7 +634,11 @@
 
         context.launchSafetyCenterActivity(withReceiverPermission = true) {
             val action = safetySourceCtsData.criticalResolvingActionWithSuccessMessage
-            waitButtonDisplayed(action.label) { it.click() }
+            waitButtonDisplayed(action.label) {
+                // Re-enable animations for this test as this is needed to show the success message.
+                setAnimationsEnabled(true)
+                it.click()
+            }
 
             // Success message should show up if issue marked as resolved
             val successMessage = action.successMessage
@@ -652,7 +685,11 @@
 
         context.launchSafetyCenterActivity(withReceiverPermission = true) {
             val action = safetySourceCtsData.criticalResolvingAction
-            waitButtonDisplayed(action.label) { it.click() }
+            waitButtonDisplayed(action.label) {
+                // Re-enable animations for this test as this is needed to show the success message.
+                setAnimationsEnabled(true)
+                it.click()
+            }
 
             waitSourceIssueNotDisplayed(safetySourceCtsData.criticalResolvingGeneralIssue)
         }
@@ -712,6 +749,52 @@
     }
 
     @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun issueCard_withAttributionTitleSetBySource_displaysAttributionTitle() {
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+
+        val data = safetySourceCtsData.informationWithIssueWithAttributionTitle
+        safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data)
+
+        context.launchSafetyCenterActivity { waitAllTextDisplayed("Attribution Title") }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun issueCard_attributionNotSetBySource_displaysGroupTitleAsAttribution() {
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+
+        val data = safetySourceCtsData.recommendationWithGeneralIssue
+        safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data)
+
+        context.launchSafetyCenterActivity { waitAllTextDisplayed("OK") }
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+    fun issueCard_attributionNotSetBySourceAndGroupTitleNull_doesNotDisplayAttributionTitle() {
+        safetyCenterCtsHelper.setConfig(ISSUE_ONLY_SOURCE_NO_GROUP_TITLE_CONFIG)
+
+        val data = SafetySourceCtsData.issuesOnly(safetySourceCtsData.recommendationGeneralIssue)
+        safetyCenterCtsHelper.setData(ISSUE_ONLY_ALL_OPTIONAL_ID, data)
+
+        context.launchSafetyCenterActivity { waitAllTextNotDisplayed("Attribution Title", "OK") }
+    }
+
+    @Test
+    @SdkSuppress(maxSdkVersion = TIRAMISU)
+    fun issueCard_attributionNotSetBySourceOnTiramisu_doesNotDisplayAttributionTitle() {
+        // TODO(b/258228790): Remove after U is no longer in pre-release
+        assumeFalse(CODENAME == "UpsideDownCake")
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+
+        val data = safetySourceCtsData.recommendationWithGeneralIssue
+        safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, data)
+
+        context.launchSafetyCenterActivity { waitAllTextNotDisplayed("Attribution title", "OK") }
+    }
+
+    @Test
     fun launchActivity_fromQuickSettings_issuesExpanded() {
         safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCES_CONFIG)
         safetyCenterCtsHelper.setData(
@@ -1120,20 +1203,23 @@
         private const val SAFETY_SOURCE_5_SUMMARY = "Safety Source 5 Summary"
 
         private fun UiDevice.rotate() {
+            unfreezeRotation()
             if (isNaturalOrientation) {
                 setOrientationLeft()
             } else {
                 setOrientationNatural()
             }
+            freezeRotation()
             waitForIdle()
         }
 
         private fun UiDevice.resetRotation() {
             if (!isNaturalOrientation) {
+                unfreezeRotation()
                 setOrientationNatural()
+                freezeRotation()
+                waitForIdle()
             }
-            unfreezeRotation()
-            waitForIdle()
         }
     }
 }
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterQsActivityTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterQsActivityTest.kt
index e3ac939..7b42e59 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterQsActivityTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterQsActivityTest.kt
@@ -32,15 +32,23 @@
 import android.support.test.uiautomator.By
 import androidx.test.core.app.ApplicationProvider.getApplicationContext
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compatibility.common.util.DisableAnimationRule
+import com.android.compatibility.common.util.FreezeRotationRule
 import org.junit.After
 import org.junit.Assume.assumeTrue
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
 /** CTS tests for the Safety Center Quick Settings Activity. */
 @RunWith(AndroidJUnit4::class)
 class SafetyCenterQsActivityTest {
+
+    @get:Rule val disableAnimationRule = DisableAnimationRule()
+
+    @get:Rule val freezeRotationRule = FreezeRotationRule()
+
     private val context: Context = getApplicationContext()
     private val safetyCenterCtsHelper = SafetyCenterCtsHelper(context)
     private val sensorPrivacyManager = context.getSystemService(SensorPrivacyManager::class.java)!!
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterStatusCardTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterStatusCardTest.kt
index ea374e4..5dea447 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterStatusCardTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterStatusCardTest.kt
@@ -35,10 +35,13 @@
 import android.support.test.uiautomator.By
 import androidx.test.core.app.ApplicationProvider.getApplicationContext
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compatibility.common.util.DisableAnimationRule
+import com.android.compatibility.common.util.FreezeRotationRule
 import com.android.safetycenter.resources.SafetyCenterResourcesContext
 import org.junit.After
 import org.junit.Assume.assumeTrue
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -46,6 +49,11 @@
 @RunWith(AndroidJUnit4::class)
 // TODO(b/244582705): Add CTS tests for device & account titles, status when unspecified entries.
 class SafetyCenterStatusCardTest {
+
+    @get:Rule val disableAnimationRule = DisableAnimationRule()
+
+    @get:Rule val freezeRotationRule = FreezeRotationRule()
+
     private val context: Context = getApplicationContext()
 
     private val safetyCenterResourcesContext = SafetyCenterResourcesContext.forTests(context)
diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterSubpagesTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterSubpagesTest.kt
new file mode 100644
index 0000000..75e85c3
--- /dev/null
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/ui/SafetyCenterSubpagesTest.kt
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.safetycenter.cts.ui
+
+import android.content.Context
+import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+import android.os.Bundle
+import android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCES_GROUP_ID
+import android.safetycenter.SafetySourceData
+import android.safetycenter.config.SafetySource
+import android.safetycenter.config.SafetySourcesGroup
+import android.safetycenter.cts.testing.SafetyCenterActivityLauncher.launchSafetyCenterActivity
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MULTIPLE_SOURCES_CONFIG
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MULTIPLE_SOURCES_GROUP_ID_1
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.MULTIPLE_SOURCE_GROUPS_CONFIG
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_CONFIG
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_ID
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SINGLE_SOURCE_INVALID_INTENT_CONFIG
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_1
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_2
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_3
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_4
+import android.safetycenter.cts.testing.SafetyCenterCtsConfigs.SOURCE_ID_5
+import android.safetycenter.cts.testing.SafetyCenterCtsHelper
+import android.safetycenter.cts.testing.SafetyCenterFlags
+import android.safetycenter.cts.testing.SafetyCenterFlags.deviceSupportsSafetyCenter
+import android.safetycenter.cts.testing.SafetySourceCtsData
+import android.safetycenter.cts.testing.UiTestHelper.waitAllTextDisplayed
+import android.safetycenter.cts.testing.UiTestHelper.waitAllTextNotDisplayed
+import android.safetycenter.cts.testing.UiTestHelper.waitButtonDisplayed
+import android.safetycenter.cts.testing.UiTestHelper.waitDisplayed
+import android.safetycenter.cts.testing.UiTestHelper.waitNotDisplayed
+import android.support.test.uiautomator.By
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import com.android.compatibility.common.util.DisableAnimationRule
+import com.android.compatibility.common.util.FreezeRotationRule
+import com.android.compatibility.common.util.UiAutomatorUtils
+import org.junit.After
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** CTS tests for generic subpages in Safety Center. */
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+class SafetyCenterSubpagesTest {
+
+    @get:Rule val disableAnimationRule = DisableAnimationRule()
+
+    @get:Rule val freezeRotationRule = FreezeRotationRule()
+
+    private val context: Context = getApplicationContext()
+    private val safetyCenterCtsHelper = SafetyCenterCtsHelper(context)
+    private val safetySourceCtsData = SafetySourceCtsData(context)
+
+    // JUnit's Assume is not supported in @BeforeClass by the CTS tests runner, so this is used to
+    // manually skip the setup and teardown methods.
+    private val shouldRunTests = context.deviceSupportsSafetyCenter()
+
+    @Before
+    fun assumeDeviceSupportsSafetyCenterToRunTests() {
+        assumeTrue(shouldRunTests)
+    }
+
+    @Before
+    fun enableSafetyCenterBeforeTest() {
+        if (!shouldRunTests) {
+            return
+        }
+        safetyCenterCtsHelper.setup()
+        SafetyCenterFlags.showSubpages = true
+    }
+
+    @After
+    fun clearDataAfterTest() {
+        if (!shouldRunTests) {
+            return
+        }
+        safetyCenterCtsHelper.reset()
+    }
+
+    @Test
+    fun launchSafetyCenter_withSubpagesIntentExtra_showsSubpageTitle() {
+        safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCE_GROUPS_CONFIG)
+        val extras = Bundle()
+        extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, MULTIPLE_SOURCES_GROUP_ID_1)
+
+        context.launchSafetyCenterActivity(extras) {
+            // CollapsingToolbar title can't be found by text, so using description instead.
+            waitDisplayed(
+                By.desc(
+                    context.getString(
+                        MULTIPLE_SOURCE_GROUPS_CONFIG.safetySourcesGroups.first()!!.titleResId)))
+        }
+    }
+
+    @Test
+    fun launchSafetyCenter_withSubpagesIntentExtraButFlagDisabled_showsHomepageTitle() {
+        SafetyCenterFlags.showSubpages = false
+        safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCE_GROUPS_CONFIG)
+        val extras = Bundle()
+        extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, MULTIPLE_SOURCES_GROUP_ID_1)
+
+        context.launchSafetyCenterActivity(extras) {
+            // CollapsingToolbar title can't be found by text, so using description instead.
+            waitDisplayed(By.desc("Security & privacy"))
+        }
+    }
+
+    @Test
+    fun launchSafetyCenter_withNonExistingGroupID_displaysNothing() {
+        safetyCenterCtsHelper.setConfig(MULTIPLE_SOURCE_GROUPS_CONFIG)
+        val extras = Bundle()
+        extras.putString(EXTRA_SAFETY_SOURCES_GROUP_ID, "non_existing_group_id")
+
+        context.launchSafetyCenterActivity(extras) {
+            waitNotDisplayed(
+                By.desc(
+                    context.getString(
+                        MULTIPLE_SOURCE_GROUPS_CONFIG.safetySourcesGroups.first()!!.titleResId)))
+        }
+    }
+
+    @Test
+    fun launchSafetyCenter_withMultipleGroups_showsHomepageEntries() {
+        val sourceCtsData = safetySourceCtsData.information
+        with(safetyCenterCtsHelper) {
+            setConfig(MULTIPLE_SOURCE_GROUPS_CONFIG)
+            setData(SOURCE_ID_1, sourceCtsData)
+            setData(SOURCE_ID_2, sourceCtsData)
+            setData(SOURCE_ID_3, sourceCtsData)
+            setData(SOURCE_ID_4, sourceCtsData)
+            setData(SOURCE_ID_5, sourceCtsData)
+        }
+        val firstGroup: SafetySourcesGroup =
+            MULTIPLE_SOURCE_GROUPS_CONFIG.safetySourcesGroups.first()
+        val lastGroup: SafetySourcesGroup = MULTIPLE_SOURCE_GROUPS_CONFIG.safetySourcesGroups.last()
+
+        context.launchSafetyCenterActivity {
+            waitAllTextDisplayed(
+                context.getString(firstGroup.titleResId),
+                context.getString(firstGroup.summaryResId),
+                context.getString(lastGroup.titleResId),
+                context.getString(lastGroup.summaryResId))
+
+            openSubpageAndExit(lastGroup) {
+                // Verifying that the subpage is opened with collapsing toolbar title
+                waitDisplayed(By.desc(context.getString(lastGroup.titleResId)))
+                waitAllTextNotDisplayed(context.getString(lastGroup.summaryResId))
+            }
+        }
+    }
+
+    @Test
+    fun launchSafetyCenter_withMultipleGroupsButFlagDisabled_showsExpandAndCollapseEntries() {
+        SafetyCenterFlags.showSubpages = false
+        val sourceCtsData = safetySourceCtsData.information
+        with(safetyCenterCtsHelper) {
+            setConfig(MULTIPLE_SOURCE_GROUPS_CONFIG)
+            setData(SOURCE_ID_1, sourceCtsData)
+            setData(SOURCE_ID_2, sourceCtsData)
+            setData(SOURCE_ID_3, sourceCtsData)
+            setData(SOURCE_ID_4, sourceCtsData)
+            setData(SOURCE_ID_5, sourceCtsData)
+        }
+        val firstGroup: SafetySourcesGroup =
+            MULTIPLE_SOURCE_GROUPS_CONFIG.safetySourcesGroups.first()
+        val lastGroup: SafetySourcesGroup = MULTIPLE_SOURCE_GROUPS_CONFIG.safetySourcesGroups.last()
+
+        context.launchSafetyCenterActivity {
+            waitAllTextDisplayed(
+                context.getString(firstGroup.titleResId),
+                context.getString(firstGroup.summaryResId),
+                context.getString(lastGroup.titleResId))
+            waitDisplayed(By.text(context.getString(lastGroup.summaryResId))) { it.click() }
+
+            // Verifying that the group is expanded and sources are displayed
+            waitAllTextDisplayed(sourceCtsData.status!!.title, sourceCtsData.status!!.summary)
+            waitAllTextNotDisplayed(context.getString(lastGroup.summaryResId))
+        }
+    }
+
+    @Test
+    fun launchSafetyCenter_redirectBackFromSubpage_showsHomepageEntries() {
+        with(safetyCenterCtsHelper) {
+            setConfig(MULTIPLE_SOURCE_GROUPS_CONFIG)
+            setData(SOURCE_ID_1, safetySourceCtsData.information)
+            setData(SOURCE_ID_2, safetySourceCtsData.information)
+        }
+        val firstGroup: SafetySourcesGroup =
+            MULTIPLE_SOURCE_GROUPS_CONFIG.safetySourcesGroups.first()
+
+        context.launchSafetyCenterActivity {
+            // Verifying that both entry title and summary are displayed on homepage
+            waitAllTextDisplayed(
+                context.getString(firstGroup.titleResId),
+                context.getString(firstGroup.summaryResId))
+
+            openSubpageAndExit(firstGroup) {
+                // Verifying that only collapsing toolbar title is displayed for subpage
+                waitDisplayed(By.desc(context.getString(firstGroup.titleResId)))
+                waitAllTextNotDisplayed(context.getString(firstGroup.summaryResId))
+            }
+
+            // Verifying that the homepage is opened again
+            waitAllTextDisplayed(
+                context.getString(firstGroup.titleResId),
+                context.getString(firstGroup.summaryResId))
+        }
+    }
+
+    @Test
+    fun entryListWithMultipleSources_clickingOnHomepageEntry_showsSubpageEntries() {
+        with(safetyCenterCtsHelper) {
+            setConfig(MULTIPLE_SOURCES_CONFIG)
+            setData(
+                SOURCE_ID_1,
+                safetySourceCtsData.buildSafetySourceDataWithSummary(
+                    severityLevel = SafetySourceData.SEVERITY_LEVEL_INFORMATION,
+                    entryTitle = SAFETY_SOURCE_1_TITLE,
+                    entrySummary = SAFETY_SOURCE_1_SUMMARY))
+            setData(
+                SOURCE_ID_2,
+                safetySourceCtsData.buildSafetySourceDataWithSummary(
+                    severityLevel = SafetySourceData.SEVERITY_LEVEL_INFORMATION,
+                    entryTitle = SAFETY_SOURCE_2_TITLE,
+                    entrySummary = SAFETY_SOURCE_2_SUMMARY))
+            setData(
+                SOURCE_ID_3,
+                safetySourceCtsData.buildSafetySourceDataWithSummary(
+                    severityLevel = SafetySourceData.SEVERITY_LEVEL_INFORMATION,
+                    entryTitle = SAFETY_SOURCE_3_TITLE,
+                    entrySummary = SAFETY_SOURCE_3_SUMMARY))
+        }
+        val firstGroup: SafetySourcesGroup = MULTIPLE_SOURCES_CONFIG.safetySourcesGroups[0]
+        val secondGroup: SafetySourcesGroup = MULTIPLE_SOURCES_CONFIG.safetySourcesGroups[1]
+
+        context.launchSafetyCenterActivity {
+            // Verifying that subpage entries of the first group are displayed
+            openSubpageAndExit(firstGroup) {
+                waitAllTextNotDisplayed(context.getString(firstGroup.summaryResId))
+                waitAllTextDisplayed(
+                    SAFETY_SOURCE_1_TITLE,
+                    SAFETY_SOURCE_1_SUMMARY,
+                    SAFETY_SOURCE_2_TITLE,
+                    SAFETY_SOURCE_2_SUMMARY)
+            }
+
+            // Verifying that subpage entries of the second group are displayed
+            openSubpageAndExit(secondGroup) {
+                waitAllTextNotDisplayed(context.getString(secondGroup.summaryResId))
+                waitAllTextDisplayed(SAFETY_SOURCE_3_TITLE, SAFETY_SOURCE_3_SUMMARY)
+            }
+        }
+    }
+
+    @Test
+    fun entryListWithSingleSource_clickingOnSubpageEntry_redirectsToDifferentScreen() {
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+        val sourcesGroup: SafetySourcesGroup = SINGLE_SOURCE_CONFIG.safetySourcesGroups.first()
+        val source: SafetySource = sourcesGroup.safetySources.first()
+
+        context.launchSafetyCenterActivity {
+            openSubpageAndExit(sourcesGroup) {
+                waitDisplayed(By.text(context.getString(source.titleResId))) { it.click() }
+                waitButtonDisplayed("Exit test activity") { it.click() }
+                waitAllTextDisplayed(
+                    context.getString(source.titleResId), context.getString(source.summaryResId))
+            }
+        }
+    }
+
+    @Test
+    fun entryListWithSingleSource_clickingTheInfoIcon_redirectsToDifferentScreen() {
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+        val sourceCtsData = safetySourceCtsData.informationWithIconAction
+        safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, sourceCtsData)
+        val sourcesGroup: SafetySourcesGroup = SINGLE_SOURCE_CONFIG.safetySourcesGroups.first()
+
+        context.launchSafetyCenterActivity {
+            openSubpageAndExit(sourcesGroup) {
+                waitDisplayed(By.desc("Information")) { it.click() }
+                waitButtonDisplayed("Exit test activity") { it.click() }
+                waitAllTextDisplayed(sourceCtsData.status!!.title, sourceCtsData.status!!.summary)
+            }
+        }
+    }
+
+    @Test
+    fun entryListWithSingleSource_clickingTheGearIcon_redirectsToDifferentScreen() {
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_CONFIG)
+        val sourceCtsData = safetySourceCtsData.informationWithGearIconAction
+        safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, sourceCtsData)
+        val sourcesGroup: SafetySourcesGroup = SINGLE_SOURCE_CONFIG.safetySourcesGroups.first()
+
+        context.launchSafetyCenterActivity {
+            openSubpageAndExit(sourcesGroup) {
+                waitDisplayed(By.desc("Settings")) { it.click() }
+                waitButtonDisplayed("Exit test activity") { it.click() }
+                waitAllTextDisplayed(sourceCtsData.status!!.title, sourceCtsData.status!!.summary)
+            }
+        }
+    }
+
+    @Test
+    fun entryListWithSingleSource_clickingSourceWithNullPendingIntent_doesNothing() {
+        safetyCenterCtsHelper.setConfig(SINGLE_SOURCE_INVALID_INTENT_CONFIG)
+        val sourceCtsData = safetySourceCtsData.informationWithNullIntent
+        safetyCenterCtsHelper.setData(SINGLE_SOURCE_ID, sourceCtsData)
+        val sourcesGroup: SafetySourcesGroup =
+            SINGLE_SOURCE_INVALID_INTENT_CONFIG.safetySourcesGroups.first()
+
+        context.launchSafetyCenterActivity {
+            openSubpageAndExit(sourcesGroup) {
+                waitDisplayed(By.text(sourceCtsData.status!!.title.toString())) { it.click() }
+
+                // Verifying that clicking on the entry doesn't redirect to any other screen
+                waitAllTextDisplayed(sourceCtsData.status!!.title, sourceCtsData.status!!.summary)
+            }
+        }
+    }
+
+    private fun openSubpageAndExit(group: SafetySourcesGroup, block: () -> Unit) {
+        val uiDevice = UiAutomatorUtils.getUiDevice()
+        uiDevice.waitForIdle()
+
+        // Opens subpage by clicking on the group title
+        waitDisplayed(By.text(context.getString(group.titleResId))) { it.click() }
+        uiDevice.waitForIdle()
+
+        // Executes the required verifications
+        block()
+        uiDevice.waitForIdle()
+
+        // Exits subpage by pressing the back button
+        uiDevice.pressBack()
+        uiDevice.waitForIdle()
+    }
+
+    companion object {
+        private const val SAFETY_SOURCE_1_TITLE = "Safety Source 1 Title"
+        private const val SAFETY_SOURCE_1_SUMMARY = "Safety Source 1 Summary"
+        private const val SAFETY_SOURCE_2_TITLE = "Safety Source 2 Title"
+        private const val SAFETY_SOURCE_2_SUMMARY = "Safety Source 2 Summary"
+        private const val SAFETY_SOURCE_3_TITLE = "Safety Source 3 Title"
+        private const val SAFETY_SOURCE_3_SUMMARY = "Safety Source 3 Summary"
+    }
+}
diff --git a/tests/functional/safetycenter/Android.bp b/tests/functional/safetycenter/Android.bp
new file mode 100644
index 0000000..fa1f03b
--- /dev/null
+++ b/tests/functional/safetycenter/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2022 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.
+//
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "SafetyCenterFunctionalTestCases",
+    defaults: ["mts-target-sdk-version-current"],
+    sdk_version: "test_current",
+    min_sdk_version: "30",
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
+    ],
+    test_suites: [
+        "general-tests",
+        "mts-permission",
+    ],
+}
diff --git a/tests/functional/safetycenter/AndroidManifest.xml b/tests/functional/safetycenter/AndroidManifest.xml
new file mode 100644
index 0000000..74e1133
--- /dev/null
+++ b/tests/functional/safetycenter/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.safetycenter.functional">
+    <application>
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:label="Functional tests for SafetyCenter"
+                     android:targetPackage="android.safetycenter.functional"/>
+</manifest>
diff --git a/tests/functional/safetycenter/AndroidTest.xml b/tests/functional/safetycenter/AndroidTest.xml
new file mode 100644
index 0000000..b8f1102
--- /dev/null
+++ b/tests/functional/safetycenter/AndroidTest.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+<configuration description="Config for functional SafetyCenter test cases">
+
+    <object
+        class="com.android.tradefed.testtype.suite.module.Sdk33ModuleController"
+        type="module_controller"/>
+
+    <option name="config-descriptor:metadata" key="component" value="framework"/>
+    <option name="config-descriptor:metadata" key="parameter"
+            value="not_instant_app"/>
+    <option name="config-descriptor:metadata" key="parameter"
+            value="not_multi_abi"/>
+    <!-- Multi-user code is tested separately using Bedstead. See SafetyCenterMultiUsersTest. -->
+    <option name="config-descriptor:metadata" key="parameter"
+            value="not_secondary_user"/>
+
+    <option name="test-suite-tag" value="functional"/>
+
+    <target_preparer
+        class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="SafetyCenterFunctionalTestCases.apk"/>
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <!-- Ensure all broadcasts are dispatched prior to running our tests, to make sure they
+             aren't polluted by `BOOT_COMPLETED` or similar broadcasts still being delivered, which
+             causes our `ActivityManager#waitForBroadcastIdle()` calls to timeout. -->
+        <option name="run-command" value="am wait-for-broadcast-idle" />
+        <!-- Disable syncing to prevent overwriting flags during testing. -->
+        <option name="run-command" value="device_config set_sync_disabled_for_tests persistent" />
+        <option name="teardown-command" value="device_config set_sync_disabled_for_tests none" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="android.safetycenter.functional"/>
+        <option name="exclude-annotation" value="org.junit.Ignore"/>
+        <option name="runtime-hint" value="5m"/>
+    </test>
+</configuration>
diff --git a/tests/functional/safetycenter/OWNERS b/tests/functional/safetycenter/OWNERS
new file mode 100644
index 0000000..5d8b816
--- /dev/null
+++ b/tests/functional/safetycenter/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 1026964
+
+include /SafetyCenter/OWNERS
diff --git a/tests/functional/safetycenter/TEST_MAPPING b/tests/functional/safetycenter/TEST_MAPPING
new file mode 100644
index 0000000..2c89f05
--- /dev/null
+++ b/tests/functional/safetycenter/TEST_MAPPING
@@ -0,0 +1,17 @@
+{
+  "presubmit": [
+    {
+      "name": "SafetyCenterFunctionalTestCases",
+      "options": [
+        {
+          "exclude-annotation": "com.android.bedstead.harrier.annotations.Postsubmit"
+        }
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "SafetyCenterFunctionalTestCases"
+    }
+  ]
+}
diff --git a/tests/functional/safetycenter/src/android/safetycenter/functional/SafetyCenterManagerTest.kt b/tests/functional/safetycenter/src/android/safetycenter/functional/SafetyCenterManagerTest.kt
new file mode 100644
index 0000000..1a60f8f
--- /dev/null
+++ b/tests/functional/safetycenter/src/android/safetycenter/functional/SafetyCenterManagerTest.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.safetycenter.functional
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Assume.assumeTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Functional tests for [SafetyCenterManager]. */
+@RunWith(AndroidJUnit4::class)
+class SafetyCenterManagerTest {
+    @Test
+    fun stub() {
+        assumeTrue(false)
+    }
+}
\ No newline at end of file
diff --git a/tests/hostside/safetycenter/Android.bp b/tests/hostside/safetycenter/Android.bp
new file mode 100644
index 0000000..33b38c6
--- /dev/null
+++ b/tests/hostside/safetycenter/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2022 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.
+//
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_host {
+    name: "SafetyCenterHostSideTestCases",
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    libs: [
+        "tradefed",
+        "junit",
+    ],
+    static_libs: [
+        "cts-statsd-atom-host-test-utils",
+    ],
+    data: [":SafetyCenterHostSideTestsHelper"],
+}
\ No newline at end of file
diff --git a/tests/hostside/safetycenter/AndroidTest.xml b/tests/hostside/safetycenter/AndroidTest.xml
new file mode 100644
index 0000000..310ef9d
--- /dev/null
+++ b/tests/hostside/safetycenter/AndroidTest.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+<configuration description="Config for Safety Center hostside tests">
+    <!-- TODO(b/239682646): Integrate these tests into MTS -->
+    <option name="config-descriptor:metadata" key="component" value="framework"/>
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app"/>
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi"/>
+    <!-- Multi-user code is tested separately using Bedstead. See SafetyCenterMultiUsersTest. -->
+    <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user"/>
+
+    <object class="com.android.tradefed.testtype.suite.module.Sdk33ModuleController"
+            type="module_controller"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <!-- Ensure all broadcasts are dispatched prior to running our tests, to make sure they
+             aren't polluted by `BOOT_COMPLETED` or similar broadcasts still being delivered, which
+             causes our `ActivityManager#waitForBroadcastIdle()` calls to timeout. -->
+        <option name="run-command" value="am wait-for-broadcast-idle" />
+        <!-- Disable syncing to prevent overwriting flags during testing. -->
+        <option name="run-command" value="device_config set_sync_disabled_for_tests persistent" />
+        <option name="teardown-command" value="device_config set_sync_disabled_for_tests none" />
+    </target_preparer>
+
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.StayAwakePreparer" />
+
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+        <option name="jar" value="SafetyCenterHostSideTestCases.jar" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/hostside/safetycenter/helper-app/Android.bp b/tests/hostside/safetycenter/helper-app/Android.bp
new file mode 100644
index 0000000..851b147
--- /dev/null
+++ b/tests/hostside/safetycenter/helper-app/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2022 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.
+//
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+    name: "SafetyCenterHostSideTestsHelper",
+    defaults: ["mts-target-sdk-version-current"],
+    sdk_version: "test_current",
+    min_sdk_version: "33",
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
+        "compatibility-device-util-axt",
+    ],
+}
\ No newline at end of file
diff --git a/tests/hostside/safetycenter/helper-app/AndroidManifest.xml b/tests/hostside/safetycenter/helper-app/AndroidManifest.xml
new file mode 100644
index 0000000..ad329bb
--- /dev/null
+++ b/tests/hostside/safetycenter/helper-app/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.safetycenter.hostside.device">
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.safetycenter.hostside.device" />
+</manifest>
\ No newline at end of file
diff --git a/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterInteractionLoggingHelperTests.kt b/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterInteractionLoggingHelperTests.kt
new file mode 100644
index 0000000..ae19dcc
--- /dev/null
+++ b/tests/hostside/safetycenter/helper-app/src/android/safetycenter/hostside/device/SafetyCenterInteractionLoggingHelperTests.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.safetycenter.hostside.device
+
+import android.content.Context
+import android.content.Intent
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compatibility.common.util.UiAutomatorUtils
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Contains "helper tests" that perform on-device setup and interaction code for Safety Center's
+ * interaction logging host-side tests. These "helper tests" just perform arrange and act steps and
+ * should not contain assertions. Assertions are performed in the host-side tests that run these
+ * helper tests.
+ *
+ * Some context: host-side tests are unable to interact with the device UI in a detailed manner, and
+ * must run "helper tests" on the device to perform in-depth interactions or setup that must be
+ * performed by code running on the device.
+ */
+@RunWith(AndroidJUnit4::class)
+class SafetyCenterInteractionLoggingHelperTests {
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Test
+    fun openSafetyCenter() {
+        context.startActivity(
+            Intent(Intent.ACTION_SAFETY_CENTER)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
+
+        // TODO(b/239682646): Use the extracted CTS support library to launch and close activity
+        // (after extracting it)
+        val uiDevice = UiAutomatorUtils.getUiDevice()
+        uiDevice.waitForIdle()
+        uiDevice.pressBack()
+    }
+}
diff --git a/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt b/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt
new file mode 100644
index 0000000..32679a1
--- /dev/null
+++ b/tests/hostside/safetycenter/src/android/safetycenter/hostside/SafetyCenterInteractionLoggingHostTest.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.safetycenter.hostside
+
+import android.cts.statsdatom.lib.AtomTestUtils
+import android.cts.statsdatom.lib.ConfigUtils
+import android.cts.statsdatom.lib.DeviceUtils
+import android.cts.statsdatom.lib.ReportUtils
+import com.android.os.AtomsProto.Atom
+import com.android.os.AtomsProto.SafetyCenterInteractionReported
+import com.android.tradefed.device.ITestDevice
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
+import com.android.tradefed.util.CommandStatus
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Host-side test for statsd interaction logging in the Safety Center UI. */
+@RunWith(DeviceJUnit4ClassRunner::class)
+class SafetyCenterInteractionLoggingHostTest : BaseHostJUnit4Test() {
+
+    private val shouldRunTests: Boolean by lazy {
+        // Device is not available when the test is first constructed
+        // TODO(b/239682646): These tests should enable safety center instead of only running when
+        // it's already enabled.
+        device.supportsSafetyCenter() && device.isSafetyCenterEnabled()
+    }
+
+    @Before
+    fun assumeDeviceSupportsSafetyCenterToRunTests() {
+        assumeTrue(shouldRunTests)
+    }
+
+    @Before
+    fun setUp() {
+        if (!shouldRunTests) return
+
+        ConfigUtils.removeConfig(device)
+        ReportUtils.clearReports(device)
+        ConfigUtils.uploadConfigForPushedAtom(
+            device,
+            getSafetyCenterPackageName(),
+            Atom.SAFETY_CENTER_INTERACTION_REPORTED_FIELD_NUMBER)
+        Thread.sleep(AtomTestUtils.WAIT_TIME_LONG.toLong())
+
+        DeviceUtils.installTestApp(device, HELPER_APK_NAME, HELPER_PACKAGE, build)
+
+        // TODO(b/239682646): Consider adding a target preparer that unlocks the device (like CTS)
+    }
+
+    @After
+    fun tearDown() {
+        if (!shouldRunTests) return
+
+        ConfigUtils.removeConfig(device)
+        ReportUtils.clearReports(device)
+        DeviceUtils.uninstallTestApp(device, HELPER_PACKAGE)
+    }
+
+    @Test
+    fun openSafetyCenter_recordsSafetyCenterViewedEvent() {
+        DeviceUtils.runDeviceTests(
+            device, HELPER_PACKAGE, HELPER_TEST_CLASS_NAME, "openSafetyCenter")
+        Thread.sleep(AtomTestUtils.WAIT_TIME_LONG.toLong()) // Wait for report to be updated
+
+        val safetyCenterViewedEvents =
+            ReportUtils.getEventMetricDataList(device).filter {
+                it.atom.safetyCenterInteractionReported.action ==
+                    SafetyCenterInteractionReported.Action.SAFETY_CENTER_VIEWED
+            }
+        assertThat(safetyCenterViewedEvents).isNotEmpty()
+    }
+
+    // TODO(b/239682646): Add more tests
+
+    private fun ITestDevice.supportsSafetyCenter(): Boolean {
+        val commandResult = executeShellV2Command("cmd safety_center supported")
+
+        if (commandResult.status != CommandStatus.SUCCESS) {
+            throw AssertionError("Unable to check if Safety Center is supported: $commandResult")
+        }
+
+        return commandResult.stdout.trim().toBoolean()
+    }
+    private fun ITestDevice.isSafetyCenterEnabled(): Boolean {
+        val commandResult = executeShellV2Command("cmd safety_center enabled")
+
+        if (commandResult.status != CommandStatus.SUCCESS) {
+            throw AssertionError("Unable to check if Safety Center is enabled: $commandResult")
+        }
+
+        return commandResult.stdout.trim().toBoolean()
+    }
+
+    private fun getSafetyCenterPackageName(): String =
+        device.executeShellCommand("cmd safety_center package-name").trim()
+
+    private companion object {
+        const val HELPER_APK_NAME = "SafetyCenterHostSideTestsHelper.apk"
+        const val HELPER_PACKAGE = "android.safetycenter.hostside.device"
+        const val HELPER_TEST_CLASS_NAME = ".SafetyCenterInteractionLoggingHelperTests"
+    }
+}
diff --git a/tests/utils/Android.bp b/tests/utils/Android.bp
index 6dd684d..cbac3dd 100644
--- a/tests/utils/Android.bp
+++ b/tests/utils/Android.bp
@@ -17,7 +17,7 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-java_library {
+android_library {
     name: "permission-apex-test-util-lib",
     sdk_version: "test_current",
     min_sdk_version: "30",
@@ -26,6 +26,7 @@
         "java/**/*.kt",
     ],
     static_libs: [
+        "androidx.test.core",
         // NOTE: when importing this library in a test you may have to add the following option to
         // the test element of your AndroidTest.xml file in order to ignore guava tests:
         // <option name="exclude-annotation" value="org.junit.Ignore"/>
diff --git a/tests/utils/AndroidManifest.xml b/tests/utils/AndroidManifest.xml
new file mode 100644
index 0000000..2d748c5
--- /dev/null
+++ b/tests/utils/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+<manifest package="com.android.permission.testing">
+    <application/>
+</manifest>
diff --git a/tests/utils/java/com/android/permission/testing/EqualsHashCodeToStringTester.kt b/tests/utils/java/com/android/permission/testing/EqualsHashCodeToStringTester.kt
index 0b8104a..066ee4d 100644
--- a/tests/utils/java/com/android/permission/testing/EqualsHashCodeToStringTester.kt
+++ b/tests/utils/java/com/android/permission/testing/EqualsHashCodeToStringTester.kt
@@ -16,53 +16,129 @@
 
 package com.android.permission.testing
 
+import android.os.Bundle
+import android.os.Parcelable
+import androidx.test.core.os.Parcelables.forceParcel
 import com.google.common.base.Equivalence
 import com.google.common.testing.EqualsTester
 import com.google.common.testing.EquivalenceTester
 
 /**
- * A class similar to [EqualsTester] that also checks that the [Object.hashCode] and
- * [Object.toString] implementations are consistent with equality groups.
+ * A class similar to [EqualsTester] that also optionally checks that the [Object.hashCode],
+ * [Object.toString] and [Parcelable] implementations are consistent with equality groups.
  *
  * Note: this class assumes that [Object.hashCode] does not create a collision for equality groups,
- * however this can be disabled by setting [hashCodeCanCollide] to `true`.
+ * however this can be disabled by setting [ignoreHashCode] to `true`.
+ *
+ * Note: this class assumes that [Object.toString] only represents the state that is used in
+ * [Object.equals] and [Object.hashCode] implementation. Objects with [Bundle] fields may break it.
+ * This can be disabled by setting [ignoreToString] to `true`.
+ *
+ * @param parcelRoundTripEqualsEquivalence optionally provide an equivalence that also checks that
+ *   the [Parcelable] implementation is consistent with equality groups by recreating equal items
+ *   from their [Parcelable] implementation
+ * @param createCopy optionally provide a custom method to create an equal copy that will be applied
+ *   to all the items in provided in an equality group
  */
-class EqualsHashCodeToStringTester(private val hashCodeCanCollide: Boolean = false) {
-    private val equalsTester = EqualsTester()
-    private val toStringTester = EquivalenceTester.of(TO_STRING_EQUIVALENCE)
-    private val hashCodeTester = EquivalenceTester.of(HASH_CODE_EQUIVALENCE)
+class EqualsHashCodeToStringTester<T>
+private constructor(
+    private val ignoreHashCode: Boolean = false,
+    private val ignoreToString: Boolean = false,
+    private val parcelRoundTripEqualsEquivalence: Equivalence<T>? = null,
+    private val createCopy: ((T) -> T)? = null
+) {
 
-    fun addEqualityGroup(vararg groups: Any): EqualsHashCodeToStringTester {
-        equalsTester.addEqualityGroup(groups)
-        toStringTester.addEquivalenceGroup(groups)
-        if (!hashCodeCanCollide) {
-            hashCodeTester.addEquivalenceGroup(groups)
-        }
+    private val equalsTester = EqualsTester()
+    private val hashCodeTester =
+        EquivalenceTester.of<T>(hashCodeEquivalence()).takeIf { !ignoreHashCode }
+    private val toStringTester =
+        EquivalenceTester.of<T>(toStringEquivalence()).takeIf { !ignoreToString }
+    private val parcelableTester =
+        parcelRoundTripEqualsEquivalence?.let { EquivalenceTester.of(it) }
+
+    fun addEqualityGroup(vararg equalItems: T): EqualsHashCodeToStringTester<T> {
+        val equalItemsWithCopiesIfNeeded = equalItems.toList().withCopiesIfNeeded(createCopy)
+        equalsTester.addEqualityGroup(*equalItemsWithCopiesIfNeeded.toAnyArray())
+        hashCodeTester?.addEquivalenceGroup(equalItemsWithCopiesIfNeeded)
+        toStringTester?.addEquivalenceGroup(equalItemsWithCopiesIfNeeded)
+        parcelableTester?.addEquivalenceGroup(equalItemsWithCopiesIfNeeded)
         return this
     }
 
     fun test() {
         equalsTester.testEquals()
-        toStringTester.test()
-        if (!hashCodeCanCollide) {
-            hashCodeTester.test()
-        }
+        hashCodeTester?.test()
+        toStringTester?.test()
+        parcelableTester?.test()
     }
 
     companion object {
 
         /**
+         * Returns an [EqualsHashCodeToStringTester] that also checks that the [Parcelable]
+         * implementation: i.e. recreating an instance from its [Parcelable] implementation returns
+         * an object that's consistent with its equality group.
+         *
+         * @see EqualsHashCodeToStringTester
+         */
+        fun <T : Parcelable> ofParcelable(
+            parcelableCreator: Parcelable.Creator<T>,
+            ignoreHashCode: Boolean = false,
+            ignoreToString: Boolean = false,
+            createCopy: ((T) -> T)? = null
+        ): EqualsHashCodeToStringTester<T> =
+            EqualsHashCodeToStringTester(
+                ignoreHashCode,
+                ignoreToString,
+                parcelRoundTripEqualsEquivalence(parcelableCreator),
+                createCopy)
+
+        /**
+         * Returns an [EqualsHashCodeToStringTester] that does not check the [Parcelable]
+         * implementation of the class, typically if the class doesn't implement [Parcelable].
+         *
+         * @see EqualsHashCodeToStringTester
+         */
+        fun <T> of(
+            ignoreHashCode: Boolean = false,
+            ignoreToString: Boolean = false,
+            createCopy: ((T) -> T)? = null
+        ): EqualsHashCodeToStringTester<T> =
+            EqualsHashCodeToStringTester(
+                ignoreHashCode, ignoreToString, parcelRoundTripEqualsEquivalence = null, createCopy)
+
+        /**
+         * An [Equivalence] that considers two instances of a class equivalent iff they are still
+         * equal when one of them is recreated from their [Parcelable] implementation.
+         */
+        private fun <T : Parcelable> parcelRoundTripEqualsEquivalence(
+            parcelableCreator: Parcelable.Creator<T>
+        ) =
+            object : Equivalence<T>() {
+
+                override fun doEquivalent(a: T, b: T): Boolean {
+                    return a.recreateFromParcel() == b && a == b.recreateFromParcel()
+                }
+
+                override fun doHash(o: T): Int {
+                    return o.recreateFromParcel().hashCode()
+                }
+
+                private fun T.recreateFromParcel(): T = forceParcel(this, parcelableCreator)
+            }
+
+        /**
          * An [Equivalence] that considers two instances of a class equivalent iff [Object.toString]
          * return the same value.
          */
-        private val TO_STRING_EQUIVALENCE =
-            object : Equivalence<Any>() {
+        private fun <T> toStringEquivalence() =
+            object : Equivalence<T>() {
 
-                override fun doEquivalent(a: Any, b: Any): Boolean {
+                override fun doEquivalent(a: T, b: T): Boolean {
                     return a.toString() == b.toString()
                 }
 
-                override fun doHash(o: Any): Int {
+                override fun doHash(o: T): Int {
                     return o.toString().hashCode()
                 }
             }
@@ -71,16 +147,21 @@
          * An [Equivalence] that considers two instances of a class equivalent iff [Object.hashCode]
          * return the same value.
          */
-        private val HASH_CODE_EQUIVALENCE =
-            object : Equivalence<Any>() {
+        private fun <T> hashCodeEquivalence() =
+            object : Equivalence<T>() {
 
-                override fun doEquivalent(a: Any, b: Any): Boolean {
+                override fun doEquivalent(a: T, b: T): Boolean {
                     return a.hashCode() == b.hashCode()
                 }
 
-                override fun doHash(o: Any): Int {
+                override fun doHash(o: T): Int {
                     return o.hashCode()
                 }
             }
+
+        private fun <T> List<T>.toAnyArray(): Array<*> = Array(size) { this[it] as Any }
+
+        private fun <T> List<T>.withCopiesIfNeeded(createCopy: ((T) -> T)? = null): List<T> =
+            createCopy?.let { this + this.map(it) } ?: this
     }
 }